diff options
Diffstat (limited to 'include')
55 files changed, 1764 insertions, 2168 deletions
diff --git a/include/AccessList.php b/include/AccessList.php deleted file mode 100644 index 43f1de111..000000000 --- a/include/AccessList.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - - -class AccessList { - - private $allow_cid; - private $allow_gid; - private $deny_cid; - private $deny_gid; - - /* indicates if we are using the default constructor values or values that have been set explicitly. */ - - private $explicit; - - 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; - } - - function get_explicit() { - return $this->explicit; - } - - /** - * Set AccessList from strings such as those in already - * existing stored data items - */ - - 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; - } - - /** - * return an array consisting of the current - * access list components where the elements - * are directly storable. - */ - - function get() { - return array( - 'allow_cid' => $this->allow_cid, - 'allow_gid' => $this->allow_gid, - 'deny_cid' => $this->deny_cid, - 'deny_gid' => $this->deny_gid, - ); - } - - /** - * Set AccessList 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. - */ - - 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; - } - - function is_private() { - return (($this->allow_cid || $this->allow_gid || $this->deny_cid || $this->deny_gid) ? true : false); - } - -} - -/** - * @brief Used to wrap ACL elements in angle brackets for storage. - * - * @param[in,out] array &$item - */ -function sanitise_acl(&$item) { - if (strlen($item)) - $item = '<' . notags(trim($item)) . '>'; - else - unset($item); -} - -/** - * @brief Convert an ACL array to a storable string. - * - * @param array $p - * @return array - */ -function perms2str($p) { - $ret = ''; - - if (is_array($p)) - $tmp = $p; - else - $tmp = explode(',', $p); - - if (is_array($tmp)) { - array_walk($tmp, 'sanitise_acl'); - $ret = implode('', $tmp); - } - - return $ret; -} - - -/** - * @brief Turn user/group ACLs stored as angle bracketed text into arrays. - * - * turn string array of angle-bracketed elements into string array - * e.g. "<123xyz><246qyo><sxo33e>" => array(123xyz,246qyo,sxo33e); - * - * @param string $s - * @return array - */ -function expand_acl($s) { - $ret = array(); - - if(strlen($s)) { - $t = str_replace('<','',$s); - $a = explode('>',$t); - foreach($a as $aa) { - if($aa) - $ret[] = $aa; - } - } - - return $ret; -} diff --git a/include/Contact.php b/include/Contact.php index 3bd5f9936..611371db6 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -331,6 +331,7 @@ function channel_remove($channel_id, $local = true, $unset_session=true) { dbesc(datetime_convert()), intval($channel_id) ); + // if this was the default channel, set another one as default if($a->account['account_default_channel'] == $channel_id) { $r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1", @@ -344,12 +345,11 @@ function channel_remove($channel_id, $local = true, $unset_session=true) { } else { $rr = q("update account set account_default_channel = 0 where account_id = %d", - intval($r[0]['channel_id']), - intval($a->account['account_id'])); + intval($a->account['account_id']) + ); } } - logger('deleting hublocs',LOGGER_DEBUG); $r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s' and hubloc_url = '%s' ", diff --git a/include/DReport.php b/include/DReport.php deleted file mode 100644 index ef86c8cbc..000000000 --- a/include/DReport.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - - -class DReport { - - private $location; - private $sender; - private $recipient; - private $message_id; - private $status; - private $date; - - function __construct($location,$sender,$recipient,$message_id,$status = 'deliver') { - $this->location = $location; - $this->sender = $sender; - $this->recipient = $recipient; - $this->message_id = $message_id; - $this->status = $status; - $this->date = datetime_convert(); - } - - function update($status) { - $this->status = $status; - $this->date = datetime_convert(); - } - - function addto_recipient($name) { - $this->recipient = $this->recipient . ' ' . $name; - } - - 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->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, - 'message_id' => $this->message_id, - 'status' => $this->status, - 'date' => $this->date - ); - } -} diff --git a/include/ItemObject.php b/include/ItemObject.php index 8be99d91e..2be7e3ac9 100644 --- a/include/ItemObject.php +++ b/include/ItemObject.php @@ -264,7 +264,7 @@ class Item extends BaseObject { if($keep_reports === 0) $keep_reports = 30; - if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) + 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'); if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) @@ -681,6 +681,11 @@ class Item extends BaseObject { $qc = ((local_channel()) ? get_pconfig(local_channel(),'system','qcomment') : null); $qcomment = (($qc) ? explode("\n",$qc) : null); + $arr = array('comment_buttons' => '','id' => $this->get_id()); + call_hooks('comment_buttons',$arr); + $comment_buttons = $arr['comment_buttons']; + + $comment_box = replace_macros($template,array( '$return_path' => '', '$threaded' => $this->is_threaded(), @@ -689,6 +694,7 @@ class Item extends BaseObject { '$id' => $this->get_id(), '$parent' => $this->get_id(), '$qcomment' => $qcomment, + '$comment_buttons' => $comment_buttons, '$profile_uid' => $conv->get_profile_owner(), '$mylink' => $observer['xchan_url'], '$mytitle' => t('This is you'), diff --git a/include/RedDAV/RedBasicAuth.php b/include/RedDAV/RedBasicAuth.php deleted file mode 100644 index 19dd9a5f0..000000000 --- a/include/RedDAV/RedBasicAuth.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief Authentication backend class for RedDAV. - * - * This class also contains some data which is not necessary for authentication - * like timezone settings. - * - * @extends Sabre\DAV\Auth\Backend\AbstractBasic - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedBasicAuth 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 - */ - protected $channel_name = null; - /** - * channel_id of the current channel of the logged-in account. - * - * @var int - */ - public $channel_id = 0; - /** - * channel_hash of the current channel of the logged-in account. - * - * @var string - */ - public $channel_hash = ''; - /** - * Set in mod/cloud.php to observer_hash. - * - * @var string - */ - public $observer = ''; - /** - * - * @see RedBrowser::set_writeable() - * @var \Sabre\DAV\Browser\Plugin - */ - public $browser; - /** - * channel_id of the current visited path. Set in RedDirectory::getDir(). - * - * @var int - */ - public $owner_id = 0; - /** - * channel_name of the current visited path. Set in RedDirectory::getDir(). - * - * Used for creating the path in cloud/ - * - * @var string - */ - public $owner_nick = ''; - /** - * Timezone from the visiting channel's channel_timezone. - * - * Used in @ref RedBrowser - * - * @var string - */ - protected $timezone = ''; - - - /** - * @brief Validates a username and password. - * - * Guest access is granted with the password "+++". - * - * @see \Sabre\DAV\Auth\Backend\AbstractBasic::validateUserPass - * @param string $username - * @param string $password - * @return bool - */ - protected function validateUserPass($username, $password) { - if (trim($password) === '+++') { - logger('guest: ' . $username); - return true; - } - - require_once('include/auth.php'); - $record = account_verify_password($username, $password); - if ($record && $record['account_default_channel']) { - $r = q("SELECT * FROM channel WHERE channel_account_id = %d AND channel_id = %d LIMIT 1", - intval($record['account_id']), - intval($record['account_default_channel']) - ); - if ($r) { - return $this->setAuthenticated($r[0]); - } - } - $r = q("SELECT * FROM channel WHERE channel_address = '%s' LIMIT 1", - dbesc($username) - ); - if ($r) { - $x = q("SELECT account_flags, account_salt, account_password FROM account WHERE account_id = %d LIMIT 1", - intval($r[0]['channel_account_id']) - ); - if ($x) { - // @fixme this foreach should not be needed? - foreach ($x as $record) { - if (($record['account_flags'] == ACCOUNT_OK) || ($record['account_flags'] == ACCOUNT_UNVERIFIED) - && (hash('whirlpool', $record['account_salt'] . $password) === $record['account_password'])) { - logger('password verified for ' . $username); - return $this->setAuthenticated($r[0]); - } - } - } - } - - $error = 'password failed for ' . $username; - logger($error); - log_failed_login($error); - - 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($r) { - $this->channel_name = $r['channel_address']; - $this->channel_id = $r['channel_id']; - $this->channel_hash = $this->observer = $r['channel_hash']; - $_SESSION['uid'] = $r['channel_id']; - $_SESSION['account_id'] = $r['channel_account_id']; - $_SESSION['authenticated'] = true; - return true; - } - - /** - * 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; - } - - /** - * @brief Sets the timezone from the channel in RedBasicAuth. - * - * 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; - } - - /** - * @brief Set browser plugin for SabreDAV. - * - * @see RedBrowser::set_writeable() - * @param \Sabre\DAV\Browser\Plugin $browser - */ - public function setBrowserPlugin($browser) { - $this->browser = $browser; - } - - /** - * @brief Prints out all RedBasicAuth 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); - } -}
\ No newline at end of file diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php deleted file mode 100644 index 1aa5f435e..000000000 --- a/include/RedDAV/RedBrowser.php +++ /dev/null @@ -1,372 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief Provides a DAV frontend for the webbrowser. - * - * RedBrowser is a SabreDAV server-plugin to provide a view to the DAV storage - * for the webbrowser. - * - * @extends \Sabre\DAV\Browser\Plugin - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedBrowser extends DAV\Browser\Plugin { - - /** - * @see set_writeable() - * @see \Sabre\DAV\Auth\Backend\BackendInterface - * @var RedBasicAuth - */ - private $auth; - - /** - * @brief Constructor for RedBrowser class. - * - * $enablePost will be activated through set_writeable() in a later stage. - * At the moment the write_storage permission is only valid for the whole - * folder. No file specific permissions yet. - * @todo disable enablePost by default and only activate if permissions - * grant edit rights. - * - * Disable assets with $enableAssets = false. Should get some thumbnail views - * anyway. - * - * @param RedBasicAuth &$auth - */ - public function __construct(&$auth) { - $this->auth = $auth; - parent::__construct(true, false); - } - - /** - * The DAV browser is instantiated after the auth module and directory classes - * but before we know the current directory and who the owner and observer - * are. So we add a pointer to the browser into the auth module and vice versa. - * Then when we've figured out what directory is actually being accessed, we - * call the following function to decide whether or not to show web elements - * which include writeable objects. - * - * @fixme It only disable/enable the visible parts. Not the POST handler - * which handels the actual requests when uploading files or creating folders. - * - * @todo Maybe this whole way of doing this can be solved with some - * $server->subscribeEvent(). - */ - public function set_writeable() { - if (! $this->auth->owner_id) { - $this->enablePost = false; - } - - if (! perm_is_allowed($this->auth->owner_id, get_observer_hash(), 'write_storage')) { - $this->enablePost = false; - } else { - $this->enablePost = true; - } - } - - /** - * @brief Creates the directory listing for the given path. - * - * @param string $path which should be displayed - */ - public function generateDirectoryIndex($path) { - // (owner_id = channel_id) is visitor owner of this directory? - $is_owner = ((local_channel() && $this->auth->owner_id == local_channel()) ? true : false); - - if ($this->auth->getTimezone()) - date_default_timezone_set($this->auth->getTimezone()); - - require_once('include/conversation.php'); - require_once('include/text.php'); - if ($this->auth->owner_nick) { - $html = profile_tabs(get_app(), (($is_owner) ? true : false), $this->auth->owner_nick); - } - - $files = $this->server->getPropertiesForPath($path, array( - '{DAV:}displayname', - '{DAV:}resourcetype', - '{DAV:}getcontenttype', - '{DAV:}getcontentlength', - '{DAV:}getlastmodified', - ), 1); - - - $parent = $this->server->tree->getNodeForPath($path); - - $parentpath = array(); - // only show parent if not leaving /cloud/; TODO how to improve this? - if ($path && $path != "cloud") { - list($parentUri) = DAV\URLUtil::splitPath($path); - $fullPath = DAV\URLUtil::encodePath($this->server->getBaseUri() . $parentUri); - - $parentpath['icon'] = $this->enableAssets ? '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl('icons/parent' . $this->iconExtension) . '" width="24" alt="' . t('parent') . '"></a>' : ''; - $parentpath['path'] = $fullPath; - } - - $f = array(); - foreach ($files as $file) { - $ft = array(); - $type = null; - - // This is the current directory, we can skip it - if (rtrim($file['href'],'/') == $path) continue; - - list(, $name) = DAV\URLUtil::splitPath($file['href']); - - if (isset($file[200]['{DAV:}resourcetype'])) { - $type = $file[200]['{DAV:}resourcetype']->getValue(); - - // resourcetype can have multiple values - if (!is_array($type)) $type = array($type); - - foreach ($type as $k=>$v) { - // Some name mapping is preferred - switch ($v) { - case '{DAV:}collection' : - $type[$k] = t('Collection'); - break; - case '{DAV:}principal' : - $type[$k] = t('Principal'); - break; - case '{urn:ietf:params:xml:ns:carddav}addressbook' : - $type[$k] = t('Addressbook'); - break; - case '{urn:ietf:params:xml:ns:caldav}calendar' : - $type[$k] = t('Calendar'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-inbox' : - $type[$k] = t('Schedule Inbox'); - break; - case '{urn:ietf:params:xml:ns:caldav}schedule-outbox' : - $type[$k] = t('Schedule Outbox'); - break; - case '{http://calendarserver.org/ns/}calendar-proxy-read' : - $type[$k] = 'Proxy-Read'; - break; - case '{http://calendarserver.org/ns/}calendar-proxy-write' : - $type[$k] = 'Proxy-Write'; - break; - } - } - $type = implode(', ', $type); - } - - // If no resourcetype was found, we attempt to use - // the contenttype property - if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { - $type = $file[200]['{DAV:}getcontenttype']; - } - if (!$type) $type = t('Unknown'); - - $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 = DAV\URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path ? $path . '/' : '') . $name, '/')); - - - $displayName = isset($file[200]['{DAV:}displayname']) ? $file[200]['{DAV:}displayname'] : $name; - - $displayName = $this->escapeHTML($displayName); - $type = $this->escapeHTML($type); - - $icon = ''; - - if ($this->enableAssets) { - $node = $this->server->tree->getNodeForPath(($path ? $path . '/' : '') . $name); - foreach (array_reverse($this->iconMap) as $class=>$iconName) { - if ($node instanceof $class) { - $icon = '<a href="' . $fullPath . '"><img src="' . $this->getAssetUrl($iconName . $this->iconExtension) . '" alt="" width="24"></a>'; - break; - } - } - } - - $parentHash = ''; - $owner = $this->auth->owner_id; - $splitPath = explode('/', $fullPath); - if (count($splitPath) > 3) { - for ($i = 3; $i < count($splitPath); $i++) { - $attachName = urldecode($splitPath[$i]); - $attachHash = $this->findAttachHash($owner, $parentHash, $attachName); - $parentHash = $attachHash; - } - } - - $attachIcon = ""; // "<a href=\"attach/".$attachHash."\" title=\"".$displayName."\"><i class=\"icon-download\"></i></a>"; - - // put the array for this file together - $ft['attachId'] = $this->findAttachIdByHash($attachHash); - $ft['fileStorageUrl'] = substr($fullPath, 0, strpos($fullPath, "cloud/")) . "filestorage/" . $this->auth->getCurrentUser(); - $ft['icon'] = $icon; - $ft['attachIcon'] = (($size) ? $attachIcon : ''); - // @todo Should this be an item value, not a global one? - $ft['is_owner'] = $is_owner; - $ft['fullPath'] = $fullPath; - $ft['displayName'] = $displayName; - $ft['type'] = $type; - $ft['size'] = $size; - $ft['sizeFormatted'] = userReadableSize($size); - $ft['lastmodified'] = (($lastmodified) ? datetime_convert('UTC', date_default_timezone_get(), $lastmodified) : ''); - $ft['iconFromType'] = getIconFromType($type); - - $f[] = $ft; - } - - $output = ''; - if ($this->enablePost) { - $this->server->broadcastEvent('onHTMLActionsPanel', array($parent, &$output)); - } - - $html .= replace_macros(get_markup_template('cloud.tpl'), array( - '$header' => t('Files') . ": " . $this->escapeHTML($path) . "/", - '$total' => t('Total'), - '$actionspanel' => $output, - '$shared' => t('Shared'), - '$create' => t('Create'), - '$upload' => t('Upload'), - '$is_owner' => $is_owner, - '$parentpath' => $parentpath, - '$entries' => $f, - '$name' => t('Name'), - '$type' => t('Type'), - '$size' => t('Size'), - '$lastmod' => t('Last Modified'), - '$parent' => t('parent'), - '$edit' => t('Edit'), - '$delete' => t('Delete'), - '$nick' => $this->auth->getCurrentUser() - )); - - $a = get_app(); - $a->page['content'] = $html; - load_pdl($a); - - $theme_info_file = "view/theme/" . current_theme() . "/php/theme.php"; - if (file_exists($theme_info_file)){ - require_once($theme_info_file); - if (function_exists(str_replace('-', '_', current_theme()) . '_init')) { - $func = str_replace('-', '_', current_theme()) . '_init'; - $func($a); - } - } - construct_page($a); - } - - /** - * @brief Creates a form to add new folders and upload files. - * - * @param \Sabre\DAV\INode $node - * @param string &$output - */ - public function htmlActionsPanel(DAV\INode $node, &$output) { - if (! $node instanceof DAV\ICollection) - return; - - // We also know fairly certain that if an object is a non-extended - // SimpleCollection, we won't need to show the panel either. - if (get_class($node) === 'Sabre\\DAV\\SimpleCollection') - return; - - // Storage and quota for the account (all channels of the owner of this directory)! - $limit = service_class_fetch($owner, 'attach_upload_limit'); - $r = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d", - intval($this->auth->channel_account_id) - ); - $used = $r[0]['total']; - if ($used) { - $quotaDesc = t('You are using %1$s of your available file storage.'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used)); - } - if ($limit && $used) { - $quotaDesc = t('You are using %1$s of %2$s available file storage. (%3$s%)'); - $quotaDesc = sprintf($quotaDesc, - userReadableSize($used), - userReadableSize($limit), - round($used / $limit, 1) * 100); - } - - // prepare quota for template - $quota = array(); - $quota['used'] = $used; - $quota['limit'] = $limit; - $quota['desc'] = $quotaDesc; - $quota['warning'] = ((($limit) && ((round($used / $limit, 1) * 100) >= 90)) ? t('WARNING:') : ''); // 10485760 bytes = 100MB - - $output .= replace_macros(get_markup_template('cloud_actionspanel.tpl'), array( - '$folder_header' => t('Create new folder'), - '$folder_submit' => t('Create'), - '$upload_header' => t('Upload file'), - '$upload_submit' => t('Upload'), - '$quota' => $quota - )); - } - - /** - * This method takes a path/name of an asset and turns it into url - * suiteable for http access. - * - * @param string $assetName - * @return string - */ - protected function getAssetUrl($assetName) { - return z_root() . '/cloud/?sabreAction=asset&assetName=' . urlencode($assetName); - } - - /** - * @brief Return the hash of an attachment. - * - * Given the owner, the parent folder and and attach name get the attachment - * hash. - * - * @param int $owner - * The owner_id - * @param string $hash - * The parent's folder hash - * @param string $attachName - * The name of the attachment - * @return string - */ - - protected function findAttachHash($owner, $parentHash, $attachName) { - $r = q("SELECT hash FROM attach WHERE uid = %d AND folder = '%s' AND filename = '%s' ORDER BY edited DESC LIMIT 1", - intval($owner), - dbesc($parentHash), - dbesc($attachName) - ); - $hash = ""; - if ($r) { - foreach ($r as $rr) { - $hash = $rr['hash']; - } - } - return $hash; - } - - /** - * @brief Returns an attachment's id for a given hash. - * - * This id is used to access the attachment in filestorage/ - * - * @param string $attachHash - * The hash of an attachment - * @return string - */ - protected function findAttachIdByHash($attachHash) { - $r = q("SELECT id FROM attach WHERE hash = '%s' ORDER BY edited DESC LIMIT 1", - dbesc($attachHash) - ); - $id = ""; - if ($r) { - foreach ($r as $rr) { - $id = $rr['id']; - } - } - return $id; - } -} diff --git a/include/RedDAV/RedDirectory.php b/include/RedDAV/RedDirectory.php deleted file mode 100644 index 8d8af5bd3..000000000 --- a/include/RedDAV/RedDirectory.php +++ /dev/null @@ -1,536 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief RedDirectory class. - * - * A class that represents a directory. - * - * @extends \Sabre\DAV\Node - * @implements \Sabre\DAV\ICollection - * @implements \Sabre\DAV\IQuota - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedDirectory extends DAV\Node implements DAV\ICollection, DAV\IQuota { - - /** - * @brief The path inside /cloud - * - * @var string - */ - 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 - */ - 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 - */ - private $os_path = ''; - - /** - * @brief Sets up the directory node, expects a full path. - * - * @param string $ext_path a full path - * @param RedBasicAuth &$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 = get_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(); - } - } - - 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(); - - 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.'); - } - - $contents = RedCollectionData($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); - - 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.'); - } - - $modulename = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - return new RedDirectory('/' . $modulename, $this->auth); - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth); - if ($x) { - return $x; - } - - 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() { - //logger(basename($this->red_path), LOGGER_DATA); - 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); - - 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.'); - } - - list($parent_path, ) = DAV\URLUtil::splitPath($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) - ); - - $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($name, LOGGER_DEBUG); - - 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.'); - } - - $mimetype = z_mime_content_type($name); - - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if (! $c) { - logger('no channel'); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $filesize = 0; - $hash = random_string(); - - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $hash; - - $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($c[0]['channel_id']) - ); - if($r) - $direct = $r[0]; - } - - 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 = $c[0]['channel_allow_cid']; - $allow_gid = $c[0]['channel_allow_gid']; - $deny_cid = $c[0]['channel_deny_cid']; - $deny_gid = $c[0]['channel_deny_gid']; - } - - $r = q("INSERT INTO attach ( aid, uid, hash, creator, filename, folder, os_storage, filetype, filesize, revision, is_photo, data, created, edited, 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' ) ", - intval($c[0]['channel_account_id']), - intval($c[0]['channel_id']), - dbesc($hash), - dbesc($this->auth->observer), - dbesc($name), - dbesc($this->folder_hash), - intval(1), - dbesc($mimetype), - intval($filesize), - intval(0), - intval($is_photo), - dbesc($f), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc($allow_cid), - dbesc($allow_gid), - dbesc($deny_cid), - dbesc($deny_gid) - ); - - - - // returns the number of bytes that were written to the file, or FALSE on failure - $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($c[0]['channel_id'], $hash); - return; - } - - // returns now - $edited = datetime_convert(); - - - - $is_photo = 0; - $x = @getimagesize($f); - logger('getimagesize: ' . print_r($x,true), LOGGER_DATA); - if(($x) && ($x[2] === IMAGETYPE_GIF || $x[2] === IMAGETYPE_JPEG || $x[2] === IMAGETYPE_PNG)) { - $is_photo = 1; - } - - - // updates entry with filesize and timestamp - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($hash), - intval($c[0]['channel_id']) - ); - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - attach_delete($c[0]['channel_id'], $hash); - return; - } - - // check against service class quota - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("SELECT SUM(filesize) AS total FROM attach WHERE aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $hash); - return; - } - } - - if($is_photo) { - $album = ''; - if($this->folder_hash) { - $f1 = q("select filename from attach WHERE hash = '%s' AND uid = %d", - dbesc($this->folder_hash), - intval($c[0]['channel_id']) - ); - if($f1) - $album = $f1[0]['filename']; - } - - require_once('include/photos.php'); - $args = array( 'resource_id' => $hash, 'album' => $album, 'os_path' => $f, 'filename' => $name, 'getimagesize' => $x, 'directory' => $direct); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - } - - /** - * @brief Creates a new subdirectory. - * - * @param string $name the directory to create - * @return void - */ - public function createDirectory($name) { - logger($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.'); - } - - $r = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - if ($r) { - $result = attach_mkdir($r[0], $this->auth->observer, array('filename' => $name, 'folder' => $this->folder_hash)); - if (! $result['success']) { - 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); - } - - - /** - * @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 = get_app()->module; - if ($this->red_path === '/' && $name === $modulename) { - //logger('We are at ' $modulename . ' show a channel list', LOGGER_DEBUG); - return true; - } - - $x = RedFileData($this->ext_path . '/' . $name, $this->auth, true); - //logger('RedFileData returns: ' . print_r($x, true), LOGGER_DATA); - if ($x) - return true; - - return false; - } - - /** - * @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 = get_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]; - - $r = q("SELECT channel_id FROM channel WHERE channel_address = '%s' AND channel_removed = 0 LIMIT 1", - dbesc($channel_name) - ); - - if (! $r) { - throw new DAV\Exception\NotFound('The file with name: ' . $channel_name . ' could not be found.'); - } - - $channel_id = $r[0]['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 Return quota usage. - * - * @fixme Should guests relly see the used/free values from filesystem of the - * complete store directory? - * - * @return array with used and free values in bytes. - */ - public function getQuotaInfo() { - // values from the filesystem of the complete <i>store/</i> directory - $limit = disk_total_space('store'); - $free = disk_free_space('store'); - - if ($this->auth->owner_id) { - $c = q("select * from channel where channel_id = %d and channel_removed = 0 limit 1", - intval($this->auth->owner_id) - ); - - $ulimit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - $limit = (($ulimit) ? $ulimit : $limit); - - $x = q("select sum(filesize) as total from attach where aid = %d", - intval($c[0]['channel_account_id']) - ); - $free = (($x) ? $limit - $x[0]['total'] : 0); - } - - return array( - $limit - $free, - $free - ); - } -}
\ No newline at end of file diff --git a/include/RedDAV/RedFile.php b/include/RedDAV/RedFile.php deleted file mode 100644 index 3283a6e88..000000000 --- a/include/RedDAV/RedFile.php +++ /dev/null @@ -1,322 +0,0 @@ -<?php - -namespace RedMatrix\RedDAV; - -use Sabre\DAV; - -/** - * @brief This class represents a file in DAV. - * - * It provides all functions to work with files in Red's cloud through DAV protocol. - * - * @extends \Sabre\DAV\Node - * @implements \Sabre\DAV\IFile - * - * @link http://github.com/friendica/red - * @license http://opensource.org/licenses/mit-license.php The MIT License (MIT) - */ -class RedFile extends DAV\Node implements DAV\IFile { - - /** - * The file from attach table. - * - * @var array - * data - * flags - * filename (string) - * filetype (string) - */ - private $data; - /** - * @see \Sabre\DAV\Auth\Backend\BackendInterface - * @var \RedMatrix\RedDAV\RedBasicAuth - */ - private $auth; - /** - * @var string - */ - private $name; - - /** - * Sets up the node, expects a full path name. - * - * @param string $name - * @param array $data from attach table - * @param &$auth - */ - public function __construct($name, $data, &$auth) { - $this->name = $name; - $this->data = $data; - $this->auth = $auth; - - logger(print_r($this->data, true), LOGGER_DATA); - } - - /** - * @brief Returns the name of the file. - * - * @return string - */ - public function getName() { - //logger(basename($this->name), LOGGER_DATA); - return basename($this->name); - } - - /** - * @brief Renames the file. - * - * @throw Sabre\DAV\Exception\Forbidden - * @param string $name The new name of the file. - * @return void - */ - public function setName($newName) { - logger('old name ' . basename($this->name) . ' -> ' . $newName, LOGGER_DATA); - - if ((! $newName) || (! $this->auth->owner_id) || (! perm_is_allowed($this->auth->owner_id, $this->auth->observer, 'write_storage'))) { - logger('permission denied '. $newName); - throw new DAV\Exception\Forbidden('Permission denied.'); - } - - $newName = str_replace('/', '%2F', $newName); - - $r = q("UPDATE attach SET filename = '%s' WHERE hash = '%s' AND id = %d", - dbesc($newName), - dbesc($this->data['hash']), - intval($this->data['id']) - ); - } - - /** - * @brief Updates the data of the file. - * - * @param resource $data - * @return void - */ - public function put($data) { - logger('put file: ' . basename($this->name), LOGGER_DEBUG); - $size = 0; - - // @todo only 3 values are needed - $c = q("SELECT * FROM channel WHERE channel_id = %d AND channel_removed = 0 LIMIT 1", - intval($this->auth->owner_id) - ); - - $is_photo = false; - $album = ''; - - $r = q("SELECT flags, folder, os_storage, filename, is_photo FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if ($r) { - if (intval($r[0]['os_storage'])) { - $d = q("select folder, data from attach where hash = '%s' and uid = %d limit 1", - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - if($d) { - if($d[0]['folder']) { - $f1 = q("select * from attach where is_dir = 1 and hash = '%s' and uid = %d limit 1", - dbesc($d[0]['folder']), - intval($c[0]['channel_id']) - ); - if($f1) { - $album = $f1[0]['filename']; - $direct = $f1[0]; - } - } - $fname = dbunescbin($d[0]['data']); - if(strpos($fname,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . $fname ; - else - $f = $fname; - - // @todo check return value and set $size directly - @file_put_contents($f, $data); - $size = @filesize($f); - logger('filename: ' . $f . ' size: ' . $size, LOGGER_DEBUG); - } - $gis = @getimagesize($f); - logger('getimagesize: ' . print_r($gis,true), LOGGER_DATA); - if(($gis) && ($gis[2] === IMAGETYPE_GIF || $gis[2] === IMAGETYPE_JPEG || $gis[2] === IMAGETYPE_PNG)) { - $is_photo = 1; - } - } - else { - // this shouldn't happen any more - $r = q("UPDATE attach SET data = '%s' WHERE hash = '%s' AND uid = %d", - dbescbin(stream_get_contents($data)), - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - $r = q("SELECT length(data) AS fsize FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - $size = $r[0]['fsize']; - } - } - } - - // returns now() - $edited = datetime_convert(); - - $d = q("UPDATE attach SET filesize = '%s', is_photo = %d, edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($size), - intval($is_photo), - dbesc($edited), - dbesc($this->data['hash']), - intval($c[0]['channel_id']) - ); - - if($is_photo) { - require_once('include/photos.php'); - $args = array( 'resource_id' => $this->data['hash'], 'album' => $album, 'os_path' => $f, 'filename' => $r[0]['filename'], 'getimagesize' => $gis, 'directory' => $direct ); - $p = photo_upload($c[0],get_app()->get_observer(),$args); - } - - // update the folder's lastmodified timestamp - $e = q("UPDATE attach SET edited = '%s' WHERE hash = '%s' AND uid = %d", - dbesc($edited), - dbesc($r[0]['folder']), - intval($c[0]['channel_id']) - ); - - // @todo do we really want to remove the whole file if an update fails - // because of maxfilesize or quota? - // There is an Exception "InsufficientStorage" or "PaymentRequired" for - // our service class from SabreDAV we could use. - - $maxfilesize = get_config('system', 'maxfilesize'); - if (($maxfilesize) && ($size > $maxfilesize)) { - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - - $limit = service_class_fetch($c[0]['channel_id'], 'attach_upload_limit'); - if ($limit !== false) { - $x = q("select sum(filesize) as total from attach where aid = %d ", - intval($c[0]['channel_account_id']) - ); - if (($x) && ($x[0]['total'] + $size > $limit)) { - logger('service class limit exceeded for ' . $c[0]['channel_name'] . ' total usage is ' . $x[0]['total'] . ' limit is ' . $limit); - attach_delete($c[0]['channel_id'], $this->data['hash']); - return; - } - } - } - - /** - * @brief Returns the raw data. - * - * @return string - */ - public function get() { - logger('get file ' . basename($this->name), LOGGER_DEBUG); - logger('os_path: ' . $this->os_path, LOGGER_DATA); - - $r = q("SELECT data, flags, os_storage, filename, filetype FROM attach WHERE hash = '%s' AND uid = %d LIMIT 1", - dbesc($this->data['hash']), - intval($this->data['uid']) - ); - if ($r) { - // @todo this should be a global definition - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - - if (in_array($r[0]['filetype'], $unsafe_types)) { - header('Content-disposition: attachment; filename="' . $r[0]['filename'] . '"'); - header('Content-type: text/plain'); - } - - if (intval($r[0]['os_storage'])) { - $x = dbunescbin($r[0]['data']); - if(strpos($x,'store') === false) - $f = 'store/' . $this->auth->owner_nick . '/' . (($this->os_path) ? $this->os_path . '/' : '') . $x; - else - $f = $x; - return fopen($f, 'rb'); - } - return dbunescbin($r[0]['data']); - } - } - - /** - * @brief Returns the ETag for a file. - * - * An ETag is a unique identifier representing the current version of the file. - * If the file changes, the ETag MUST change. - * The ETag is an arbitrary string, but MUST be surrounded by double-quotes. - * - * Return null if the ETag can not effectively be determined. - * - * @return null|string - */ - public function getETag() { - $ret = null; - if ($this->data['hash']) { - $ret = '"' . $this->data['hash'] . '"'; - } - return $ret; - } - - /** - * @brief Returns the mime-type for a file. - * - * If null is returned, we'll assume application/octet-stream - * - * @return mixed - */ - public function getContentType() { - // @todo this should be a global definition. - $unsafe_types = array('text/html', 'text/css', 'application/javascript'); - if (in_array($this->data['filetype'], $unsafe_types)) { - return 'text/plain'; - } - return $this->data['filetype']; - } - - /** - * @brief Returns the size of the node, in bytes. - * - * @return int - * filesize in bytes - */ - public function getSize() { - return $this->data['filesize']; - } - - /** - * @brief Returns the last modification time for the file, as a unix - * timestamp. - * - * @return int last modification time in UNIX timestamp - */ - public function getLastModified() { - return datetime_convert('UTC', 'UTC', $this->data['edited'], 'U'); - } - - /** - * @brief Delete the file. - * - * This method checks the permissions and then calls attach_delete() function - * to actually remove the file. - * - * @throw \Sabre\DAV\Exception\Forbidden - */ - public function delete() { - logger('delete file ' . basename($this->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.'); - } - - 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->data['hash']); - } -} diff --git a/include/account.php b/include/account.php index e448bdcc6..4c828003e 100644 --- a/include/account.php +++ b/include/account.php @@ -11,6 +11,7 @@ require_once('include/text.php'); require_once('include/language.php'); require_once('include/datetime.php'); require_once('include/crypto.php'); +require_once('include/identity.php'); function check_account_email($email) { @@ -329,7 +330,7 @@ function send_reg_approval_email($arr) { return($delivered ? true : false); } -function send_verification_email($email,$password) { +function send_register_success_email($email,$password) { $email_msg = replace_macros(get_intltext_template('register_open_eml.tpl'), array( '$sitename' => get_config('system','sitename'), @@ -353,7 +354,7 @@ function send_verification_email($email,$password) { * @param string $hash * @return array|boolean */ -function user_allow($hash) { +function account_allow($hash) { $ret = array('success' => false); @@ -406,6 +407,9 @@ function user_allow($hash) { pop_lang(); + if(get_config('system','auto_channel_create') || UNO) + auto_channel_create($register[0]['uid']); + if ($res) { info( t('Account approved.') . EOL ); return true; @@ -414,7 +418,7 @@ function user_allow($hash) { /** - * @brief Denies a user registration. + * @brief Denies an account registration. * * This does not have to go through user_remove() and save the nickname * permanently against re-registration, as the person was not yet @@ -423,7 +427,8 @@ function user_allow($hash) { * @param string $hash * @return boolean */ -function user_deny($hash) { + +function account_deny($hash) { $register = q("SELECT * FROM register WHERE hash = '%s' LIMIT 1", dbesc($hash) @@ -452,11 +457,14 @@ function user_deny($hash) { } +// called from regver to activate an account from the email verification link -function user_approve($hash) { +function account_approve($hash) { $ret = array('success' => false); + // 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) ); @@ -491,6 +499,10 @@ function user_approve($hash) { intval($register[0]['uid']) ); + + if(get_config('system','auto_channel_create') || UNO) + auto_channel_create($register[0]['uid']); + info( t('Account verified. Please login.') . EOL ); return true; diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 4d44ec12e..3c8f34321 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -210,10 +210,13 @@ function fixacl(&$item) { $item = str_replace(array('<','>'),array('',''),$item); } -function populate_acl($defaults = null,$show_jotnets = true) { +function populate_acl($defaults = null,$show_jotnets = true, $showall = '') { $allow_cid = $allow_gid = $deny_cid = $deny_gid = false; + if(! $showall) + $showall = t('Visible to your default audience'); + if(is_array($defaults)) { $allow_cid = ((strlen($defaults['allow_cid'])) ? explode('><', $defaults['allow_cid']) : array() ); @@ -231,22 +234,21 @@ function populate_acl($defaults = null,$show_jotnets = true) { $jotnets = ''; if($show_jotnets) { -logger('jot_networks'); call_hooks('jot_networks', $jotnets); } $tpl = get_markup_template("acl_selector.tpl"); $o = replace_macros($tpl, array( - '$showall'=> t("Visible to your default audience"), - '$show' => t("Show"), - '$hide' => t("Don't show"), - '$allowcid' => json_encode($allow_cid), - '$allowgid' => json_encode($allow_gid), - '$denycid' => json_encode($deny_cid), - '$denygid' => json_encode($deny_gid), - '$jnetModalTitle' => t('Other networks and post services'), - '$jotnets' => $jotnets, - '$aclModalTitle' => t('Permissions'), + '$showall' => $showall, + '$show' => t("Show"), + '$hide' => t("Don't show"), + '$allowcid' => json_encode($allow_cid), + '$allowgid' => json_encode($allow_gid), + '$denycid' => json_encode($deny_cid), + '$denygid' => json_encode($deny_gid), + '$jnetModalTitle' => t('Other networks and post services'), + '$jotnets' => $jotnets, + '$aclModalTitle' => t('Permissions'), '$aclModalDismiss' => t('Close') )); diff --git a/include/api.php b/include/api.php index 5053977c5..e78e6cc37 100644 --- a/include/api.php +++ b/include/api.php @@ -2106,10 +2106,10 @@ require_once('include/api_auth.php'); 'private' => $private, 'textlimit' => $textlimit, 'sslserver' => $sslserver, 'ssl' => $ssl, 'shorturllength' => '30', 'hubzilla' => array( - 'PLATFORM_NAME' => PLATFORM_NAME, - 'RED_VERSION' => RED_VERSION, + 'PLATFORM_NAME' => Zotlabs\Project\System::get_platform_name(), + 'RED_VERSION' => Zotlabs\Project\System::get_project_version(), 'ZOT_REVISION' => ZOT_REVISION, - 'DB_UPDATE_VERSION' => DB_UPDATE_VERSION + 'DB_UPDATE_VERSION' => Zotlabs\Project\System::get_update_version() ) )); @@ -2142,12 +2142,12 @@ require_once('include/api_auth.php'); if($type === 'xml') { header("Content-type: application/xml"); - echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . RED_VERSION . '</version>' . "\r\n"; + echo '<?xml version="1.0" encoding="UTF-8"?>' . "\r\n" . '<version>' . Zotlabs\Project\System::get_project_version() . '</version>' . "\r\n"; killme(); } elseif($type === 'json') { header("Content-type: application/json"); - echo '"' . RED_VERSION . '"'; + echo '"' . Zotlabs\Project\System::get_project_version() . '"'; killme(); } } diff --git a/include/attach.php b/include/attach.php index 8595d5d86..2777b5813 100644 --- a/include/attach.php +++ b/include/attach.php @@ -13,6 +13,7 @@ require_once('include/permissions.php'); require_once('include/security.php'); +require_once('include/group.php'); /** * @brief Guess the mimetype from file ending. @@ -1242,7 +1243,7 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { $channel_address = (($c) ? $c[0]['channel_address'] : 'notfound'); $photo_sql = (($is_photo) ? " and is_photo = 1 " : ''); - $r = q("SELECT hash, flags, is_dir, is_photo, folder FROM attach WHERE hash = '%s' AND uid = %d $photo_sql limit 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) ); @@ -1313,7 +1314,9 @@ function attach_delete($channel_id, $resource, $is_photo = 0) { intval($channel_id) ); - file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', $notify=0); + file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', $notify=1); + + return; } /** @@ -1468,7 +1471,7 @@ function pipe_streams($in, $out) { * @param string $deny_cid * @param string $deny_gid * @param string $verb - * @param boolean $no_activity + * @param boolean $notify */ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $deny_gid, $verb, $notify) { @@ -1514,13 +1517,21 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, $mid = item_message_id(); - $arr = array(); + $objtype = ACTIVITY_OBJ_FILE; + $arr = array(); + $arr['aid'] = get_account_id(); + $arr['uid'] = $channel_id; $arr['item_wall'] = 1; $arr['item_origin'] = 1; $arr['item_unseen'] = 1; - - $objtype = ACTIVITY_OBJ_FILE; + $arr['author_xchan'] = $poster['xchan_hash']; + $arr['owner_xchan'] = $poster['xchan_hash']; + $arr['title'] = ''; + $arr['item_hidden'] = 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); @@ -1548,36 +1559,27 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, } + //send update activity and create a new one if($update && $verb == 'post' ) { - //send update activity and create a new one - //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); + $u_mid = item_message_id(); - $arr['aid'] = get_account_id(); - $arr['uid'] = $channel_id; $arr['mid'] = $u_mid; $arr['parent_mid'] = $u_mid; - $arr['author_xchan'] = $poster['xchan_hash']; - $arr['owner_xchan'] = $poster['xchan_hash']; - $arr['title'] = ''; - //updates should be visible to everybody -> perms may have changed - $arr['allow_cid'] = ''; - $arr['allow_gid'] = ''; - $arr['deny_cid'] = ''; - $arr['deny_gid'] = ''; - $arr['item_hidden'] = 1; - $arr['item_private'] = 0; + $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_type'] = $objtype; $arr['object'] = $u_jsonobject; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; $arr['body'] = ''; $post = item_store($arr); @@ -1593,32 +1595,25 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid, //notice( t('File activity updated') . EOL); } + //don't create new activity if notify was not enabled if(! $notify) { return; } - $arr = array(); + //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['aid'] = get_account_id(); - $arr['uid'] = $channel_id; $arr['mid'] = $mid; $arr['parent_mid'] = $mid; - $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'] = ''; $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_hidden'] = 1; $arr['item_private'] = $private; $arr['verb'] = (($update) ? ACTIVITY_UPDATE : ACTIVITY_POST); - $arr['obj_type'] = $objtype; - $arr['resource_id'] = $object['hash']; - $arr['resource_type'] = 'attach'; $arr['object'] = (($update) ? $u_jsonobject : $jsonobject); $arr['body'] = ''; @@ -1705,7 +1700,7 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny //turn allow_gid into allow_cid's foreach($arr_allow_gid as $gid) { - $in_group = in_group($gid); + $in_group = group_get_members($gid); $arr_allow_cid = array_unique(array_merge($arr_allow_cid, $in_group)); } @@ -1727,7 +1722,7 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny * */ if($parent_arr['allow_gid']) { foreach($parent_arr['allow_gid'][$count] as $gid) { - $in_group = in_group($gid); + $in_group = group_get_members($gid); $parent_arr['allow_cid'][$count] = array_unique(array_merge($parent_arr['allow_cid'][$count], $in_group)); } } @@ -1808,31 +1803,6 @@ function recursive_activity_recipients($arr_allow_cid, $arr_allow_gid, $arr_deny return $ret; } -/** - * @brief Returns members of a group. - * - * @param int $group_id id of the group to look up - */ -function in_group($group_id) { - $group_members = array(); - - /** @TODO make these two queries one with a join. */ - $x = q("SELECT id FROM groups WHERE hash = '%s'", - dbesc($group_id) - ); - - $r = q("SELECT xchan FROM group_member WHERE gid = %d", - intval($x[0]['id']) - ); - - foreach($r as $ig) { - $group_members[] = $ig['xchan']; - } - - return $group_members; -} - - function filepath_macro($s) { return str_replace( diff --git a/include/bbcode.php b/include/bbcode.php index a8372d728..477436475 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -684,7 +684,11 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $cache = false) } // Check for centered text if (strpos($Text,'[/center]') !== false) { - $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $Text); + $Text = preg_replace("(\[center\](.*?)\[\/center\])ism", "<div style=\"text-align:center;\">$1</div>", $Text); + } + // Check for footer + if (strpos($Text,'[/footer]') !== false) { + $Text = preg_replace("(\[footer\](.*?)\[\/footer\])ism", "<div class=\"wall-item-footer\">$1</div>", $Text); } // Check for list text $Text = str_replace("[*]", "<li>", $Text); diff --git a/include/cli_startup.php b/include/cli_startup.php index 027d62953..70ab1a24a 100644 --- a/include/cli_startup.php +++ b/include/cli_startup.php @@ -15,6 +15,9 @@ function cli_startup() { if(is_null($db)) { @include(".htconfig.php"); + if(! defined('UNO')) + define('UNO', 0); + $a->timezone = ((x($default_timezone)) ? $default_timezone : 'UTC'); date_default_timezone_set($a->timezone); diff --git a/include/comanche.php b/include/comanche.php index 1537226ca..ddf331321 100644 --- a/include/comanche.php +++ b/include/comanche.php @@ -275,6 +275,7 @@ function comanche_widget($name, $text) { $vars = array(); $matches = array(); + $cnt = preg_match_all("/\[var=(.*?)\](.*?)\[\/var\]/ism", $text, $matches, PREG_SET_ORDER); if ($cnt) { foreach ($matches as $mtch) { @@ -287,8 +288,7 @@ function comanche_widget($name, $text) { if(! function_exists($func)) { if(file_exists('widget/' . trim($name) . '.php')) require_once('widget/' . trim($name) . '.php'); - elseif(folder_exists('widget/'. trim($name)) - && (file_exists('widget/' . trim($name) . '/' . trim($name) . '.php'))) + elseif(file_exists('widget/' . trim($name) . '/' . trim($name) . '.php')) require_once('widget/' . trim($name) . '/' . trim($name) . '.php'); } else { diff --git a/include/config.php b/include/config.php index c94d25eb8..51d4e99ac 100644 --- a/include/config.php +++ b/include/config.php @@ -531,3 +531,86 @@ function del_xconfig($xchan, $family, $key) { ); return $ret; } + + +// account configuration storage is built on top of the under-utilised xconfig + +function load_aconfig($account_id) { + load_xconfig('a_' . $account_id); +} + +function get_aconfig($account_id, $family, $key) { + return get_xconfig('a_' . $account_id, $family, $key); +} + +function set_aconfig($account_id, $family, $key, $value) { + return set_xconfig('a_' . $account_id, $family, $key, $value); +} + +function del_aconfig($account_id, $family, $key) { + return del_xconfig('a_' . $account_id, $family, $key); +} + + +function load_abconfig($chash,$xhash) { + $r = q("select * from abconfig where chan = '%s' and xchan = '%s'", + dbesc($chash), + dbesc($xhash) + ); + return $r; +} + +function get_abconfig($chash,$xhash,$family,$key) { + $r = q("select * from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' limit 1", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + if($r) { + return ((preg_match('|^a:[0-9]+:{.*}$|s', $r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); + } + return false; +} + + +function set_abconfig($chash,$xhash,$family,$key,$value) { + + $dbvalue = ((is_array($value)) ? serialize($value) : $value); + $dbvalue = ((is_bool($dbvalue)) ? intval($dbvalue) : $dbvalue); + + if(get_abconfig($chash,$xhash,$family,$key) === false) { + $r = q("insert into abconfig ( chan, xchan, cat, k, v ) values ( '%s', '%s', '%s', '%s', '%s' ) ", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key), + dbesc($dbvalue) + ); + } + else { + $r = q("update abconfig set v = '%s' where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", + dbesc($dbvalue), + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + } + if($r) + return $value; + return false; +} + + +function del_abconfig($chash,$xhash,$family,$key) { + + $r = q("delete from abconfig where chan = '%s' and xchan = '%s' and cat = '%s' and k = '%s' ", + dbesc($chash), + dbesc($xhash), + dbesc($family), + dbesc($key) + ); + + return $r; +} diff --git a/include/contact_selectors.php b/include/contact_selectors.php index 8671f1bd1..d44bee784 100644 --- a/include/contact_selectors.php +++ b/include/contact_selectors.php @@ -79,7 +79,7 @@ function network_to_name($s) { NETWORK_MAIL => t('Email'), NETWORK_DIASPORA => t('Diaspora'), NETWORK_FACEBOOK => t('Facebook'), - NETWORK_ZOT => t('Zot!'), + NETWORK_ZOT => t('Zot'), NETWORK_LINKEDIN => t('LinkedIn'), NETWORK_XMPP => t('XMPP/IM'), NETWORK_MYSPACE => t('MySpace'), diff --git a/include/conversation.php b/include/conversation.php index 747bb5d0a..39119b2bb 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -942,25 +942,35 @@ function item_photo_menu($item){ $clean_url = normalise_link($item['author-link']); } - $menu = Array( + $poco_rating = get_config('system','poco_rating_enable'); + // if unset default to enabled + if($poco_rating === false) + $poco_rating = true; + + $ratings_url = (($poco_rating) ? z_root() . '/ratings/' . urlencode($item['author_xchan']) : ''); + + $post_menu = Array( t("View Source") => $vsrc_link, t("Follow Thread") => $sub_link, t("Unfollow Thread") => $unsub_link, - t("View Status") => $status_link, + ); + + $author_menu = array( t("View Profile") => $profile_link, - t("View Photos") => $photos_link, t("Activity/Posts") => $posts_link, t("Connect") => $follow_url, t("Edit Connection") => $contact_url, - t("Send PM") => $pm_url, + t("Message") => $pm_url, + t('Ratings') => $ratings_url, t("Poke") => $poke_link ); - $args = array('item' => $item, 'menu' => $menu); + + $args = array('item' => $item, 'post_menu' => $post_menu, 'author_menu' => $author_menu); call_hooks('item_photo_menu', $args); - $menu = $args['menu']; + $menu = array_merge($args['post_menu'],$args['author_menu']); $o = ""; foreach($menu as $k=>$v){ @@ -1585,8 +1595,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ if (is_null($nickname)) $nickname = $channel['channel_address']; + $uid = (($a->profile['profile_uid']) ? $a->profile['profile_uid'] : local_channel()); + if($uid == local_channel()) { + $cal_link = ''; + } + else { + $cal_link = '/cal/' . $nickname; + } + + if (get_pconfig($uid, 'system', 'noprofiletabs')) return; @@ -1634,6 +1653,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){ ); } + if($p['view_stream'] && $cal_link) { + $tabs[] = array( + 'label' => t('Events'), + 'url' => $a->get_baseurl() . $cal_link, + 'sel' => ((argv(0) == 'cal' || argv(0) == 'events') ? 'active' : ''), + 'title' => t('Events'), + 'id' => 'event-tab', + ); + } + + if ($p['chat']) { require_once('include/chat.php'); $has_chats = chatroom_list_count($uid); diff --git a/include/crypto.php b/include/crypto.php index 494a2a5b9..94a6b4a58 100644 --- a/include/crypto.php +++ b/include/crypto.php @@ -20,7 +20,22 @@ function rsa_verify($data,$sig,$key,$alg = 'sha256') { if(intval(OPENSSL_ALGO_SHA256) && $alg === 'sha256') $alg = OPENSSL_ALGO_SHA256; - $verify = openssl_verify($data,$sig,$key,$alg); + $verify = @openssl_verify($data,$sig,$key,$alg); + + if(! $verify) { + logger('openssl_verify: ' . openssl_error_string(),LOGGER_NORMAL,LOG_ERR); + logger('openssl_verify: key: ' . $key, LOGGER_DEBUG, LOG_ERR); + // provide a backtrace so that we can debug key issues + if(version_compare(PHP_VERSION, '5.4.0') >= 0) { + $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + if($stack) { + foreach($stack as $s) { + logger('stack: ' . basename($s['file']) . ':' . $s['line'] . ':' . $s['function'] . '()',LOGGER_DEBUG,LOG_ERR); + } + } + } + } + return $verify; } diff --git a/include/datetime.php b/include/datetime.php index 1d10e7ad7..57b2b6d37 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -129,7 +129,7 @@ function dob($dob) { else $value = (($year) ? datetime_convert('UTC','UTC',$dob,'Y-m-d') : datetime_convert('UTC','UTC',$dob,'m-d')); - $o = '<input type="text" name="dob" value="' . $value . '" placeholder="' . t('YYYY-MM-DD or MM-DD') . '" />'; + $o = '<input class="form-control" type="text" name="dob" value="' . $value . '" placeholder="' . t('YYYY-MM-DD or MM-DD') . '" />'; // if ($dob && $dob != '0000-00-00') // $o = datesel($f,mktime(0,0,0,0,0,1900),mktime(),mktime(0,0,0,$month,$day,$year),'dob'); @@ -269,15 +269,16 @@ function relative_date($posted_date, $format = null) { return t('less than a second ago'); } - $a = array( 12 * 30 * 24 * 60 * 60 => array( t('year'), t('years')), - 30 * 24 * 60 * 60 => array( t('month'), t('months')), - 7 * 24 * 60 * 60 => array( t('week'), t('weeks')), - 24 * 60 * 60 => array( t('day'), t('days')), - 60 * 60 => array( t('hour'), t('hours')), - 60 => array( t('minute'), t('minutes')), - 1 => array( t('second'), t('seconds')) + $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) { @@ -285,11 +286,43 @@ function relative_date($posted_date, $format = null) { if (! $format) $format = t('%1$d %2$s ago', 'e.g. 22 hours ago, 1 minute ago'); - return sprintf($format, $r, (($r == 1) ? $str[0] : $str[1])); + return sprintf($format, $r, plural_dates($str,$r)); } } } +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; + } +} + + + + /** * @brief Returns timezone correct age in years. * @@ -516,7 +549,7 @@ function update_birthdays() { $ev['event_xchan'] = $rr['xchan_hash']; $ev['start'] = datetime_convert('UTC', 'UTC', $rr['abook_dob']); $ev['finish'] = datetime_convert('UTC', 'UTC', $rr['abook_dob'] . ' + 1 day '); - $ev['adjust'] = 1; + $ev['adjust'] = intval(feature_enabled($rr['abook_channel'],'smart_birthdays')); $ev['summary'] = sprintf( t('%1$s\'s birthday'), $rr['xchan_name']); $ev['description'] = sprintf( t('Happy Birthday %1$s'), '[zrl=' . $rr['xchan_url'] . ']' . $rr['xchan_name'] . '[/zrl]') ; diff --git a/include/dba/dba_driver.php b/include/dba/dba_driver.php index 1fb3d5c00..3c5b0b67e 100755 --- a/include/dba/dba_driver.php +++ b/include/dba/dba_driver.php @@ -303,9 +303,9 @@ function q($sql) { if($stmt === false) { if(version_compare(PHP_VERSION, '5.4.0') >= 0) logger('dba: vsprintf error: ' . - print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true)); + print_r(debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1), true),LOGGER_NORMAL,LOG_CRIT); else - logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true)); + logger('dba: vsprintf error: ' . print_r(debug_backtrace(), true),LOGGER_NORMAL,LOG_CRIT); } return $db->q($stmt); } @@ -314,7 +314,7 @@ function q($sql) { * This will happen occasionally trying to store the * session data after abnormal program termination */ - logger('dba: no database: ' . print_r($args,true)); + logger('dba: no database: ' . print_r($args,true),LOGGER_NORMAL,LOG_CRIT); return false; } @@ -385,6 +385,7 @@ function db_getfunc($f) { if(isset($lookup[$f]) && isset($lookup[$f][ACTIVE_DBTYPE])) return $lookup[$f][ACTIVE_DBTYPE]; - logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG); + logger('Unable to abstract DB function "'. $f . '" for dbtype ' . ACTIVE_DBTYPE, LOGGER_DEBUG, LOG_ERR); return $f; } + diff --git a/include/dba/dba_mysqli.php b/include/dba/dba_mysqli.php index 74a999974..6986d4586 100755 --- a/include/dba/dba_mysqli.php +++ b/include/dba/dba_mysqli.php @@ -32,7 +32,7 @@ class dba_mysqli extends dba_driver { if($this->error) { - logger('dba_mysqli: ERROR: ' . printable($sql) . "\n" . $this->error); + logger('dba_mysqli: 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); } @@ -40,13 +40,13 @@ class dba_mysqli extends dba_driver { if(($result === true) || ($result === false)) { if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false')); + logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returns ' . (($result) ? 'true' : 'false'), LOGGER_NORMAL,(($result) ? LOG_INFO : LOG_ERR)); } return $result; } if($this->debug) { - logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.'); + logger('dba_mysqli: DEBUG: ' . printable($sql) . ' returned ' . $result->num_rows . ' results.', LOGGER_NORMAL, LOG_INFO); } $r = array(); @@ -55,7 +55,7 @@ class dba_mysqli extends dba_driver { $r[] = $x; $result->free_result(); if($this->debug) { - logger('dba_mysqli: ' . printable(print_r($r,true))); + logger('dba_mysqli: ' . printable(print_r($r,true)), LOGGER_NORMAL, LOG_INFO); } } return $r; diff --git a/include/deliver.php b/include/deliver.php index 7ff0fa125..40df543d5 100644 --- a/include/deliver.php +++ b/include/deliver.php @@ -31,6 +31,7 @@ function deliver_run($argv, $argc) { if(($r[0]['outq_posturl'] === z_root() . '/post') && ($r[0]['outq_msg'])) { logger('deliver: local delivery', LOGGER_DEBUG); + // local delivery // we should probably batch these and save a few delivery processes @@ -72,7 +73,7 @@ function deliver_run($argv, $argc) { } } - // otherwise it's a remote delivery - call queue_deliver(); + // otherwise it's a remote delivery - call queue_deliver() with the $immediate flag queue_deliver($r[0],true); diff --git a/include/enotify.php b/include/enotify.php index c9b6e0463..e182377c0 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -273,14 +273,14 @@ function notification($params) { $preamble = sprintf( t('%1$s, you\'ve received an new connection request from \'%2$s\' at %3$s'), $recip['channel_name'], $sender['xchan_name'], $sitename); $epreamble = sprintf( t('%1$s, you\'ve received [zrl=%2$s]a new connection request[/zrl] from %3$s.'), $recip['channel_name'], - $itemlink, + $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 ); - $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '">' . $sitename . '</a>'); - $itemlink = $params['link']; + $tsitelink = sprintf( $sitelink, $siteurl . '/connections/ifpending'); + $hsitelink = sprintf( $sitelink, '<a href="' . $siteurl . '/connections/ifpending">' . $sitename . '</a>'); + $itemlink = $params['link']; } if ($params['type'] == NOTIFY_SUGGEST) { @@ -529,6 +529,7 @@ function notification($params) { $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( '$banner' => $datarray['banner'], + '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), '$product' => $datarray['product'], '$preamble' => $datarray['preamble'], '$sitename' => $datarray['sitename'], diff --git a/include/event.php b/include/event.php index 539bfe484..2969f4f61 100644 --- a/include/event.php +++ b/include/event.php @@ -67,7 +67,7 @@ function ical_wrapper($ev) { $o .= "BEGIN:VCALENDAR"; $o .= "\r\nVERSION:2.0"; $o .= "\r\nMETHOD:PUBLISH"; - $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . PLATFORM_NAME . "//" . strtoupper(get_app()->language). "\r\n"; + $o .= "\r\nPRODID:-//" . get_config('system','sitename') . "//" . Zotlabs\Project\System::get_platform_name() . "//" . strtoupper(get_app()->language). "\r\n"; if(array_key_exists('start', $ev)) $o .= format_event_ical($ev); else { @@ -440,6 +440,17 @@ function event_addtocal($item_id, $uid) { $ev['event_hash'] = $item['resource_id']; } + if($ev->private) + $ev['allow_cid'] = '<' . $channel['channel_hash'] . '>'; + else { + $acl = new Zotlabs\Access\AccessList($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", diff --git a/include/features.php b/include/features.php index 4e962b00e..ff6b71d4c 100644 --- a/include/features.php +++ b/include/features.php @@ -5,12 +5,19 @@ */ + + + function feature_enabled($uid,$feature) { - $x = get_pconfig($uid,'feature',$feature); + + $x = get_config('feature_lock',$feature); if($x === false) { - $x = get_config('feature',$feature); - if($x === false) - $x = get_feature_default($feature); + $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); @@ -18,7 +25,7 @@ function feature_enabled($uid,$feature) { } function get_feature_default($feature) { - $f = get_features(); + $f = get_features(false); foreach($f as $cat) { foreach($cat as $feat) { if(is_array($feat) && $feat[0] === $feature) @@ -29,66 +36,92 @@ function get_feature_default($feature) { } -function get_features() { +function get_features($filtered = true) { + + if(UNO && $filtered) + return array(); $arr = array( // General 'general' => array( t('General Features'), -// This is per post, and different from fixed expiration 'expire' which isn't working yet - array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time'), false), - array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles'), false), - array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false), - array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false), - array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false), - array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders'),false), - array('nav_channel_select', t('Navigation Channel Select'), t('Change channels directly from within the navigation dropdown menu'),false), - array('photo_location', t('Photo Location'), t('If location data is available on uploaded photos, link this to a map.'),false), - - array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options'),false), - array('premium_channel', t('Premium Channel'), t('Allows you to set restrictions and terms on those that connect with your channel'),false), + // This is per post, and different from fixed expiration 'expire' which isn't working yet + array('content_expire', t('Content Expiration'), t('Remove posts/comments and/or private messages at a future time'), false, get_config('feature_lock','content_expire')), + array('multi_profiles', t('Multiple Profiles'), t('Ability to create multiple profiles'), false, get_config('feature_lock','multi_profiles')), + array('advanced_profiles', t('Advanced Profiles'), t('Additional profile sections and selections'),false,get_config('feature_lock','advanced_profiles')), + array('profile_export', t('Profile Import/Export'), t('Save and load profile details across sites/channels'),false,get_config('feature_lock','profile_export')), + array('webpages', t('Web Pages'), t('Provide managed web pages on your channel'),false,get_config('feature_lock','webpages')), + array('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')), + array('private_notes', t('Private Notes'), t('Enables a tool to store notes and reminders (note: not encrypted)'),false,get_config('feature_lock','private_notes')), + array('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')), + array('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')), + + array('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')), + array('expert', t('Expert Mode'), t('Enable Expert Mode to provide advanced configuration options'),false,get_config('feature_lock','expert')), + array('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')), ), // Post composition 'composition' => array( t('Post Composition Features'), -// array('richtext', t('Richtext Editor'), t('Enable richtext editor'),false), - array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts'),false), - array('large_photos', t('Large Photos'), t('Include large (1024px) photo thumbnails in posts. If not enabled, use small (640px) photo thumbnails'),false), - array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds'),false), - array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key'),false), - array('consensus_tools', t('Enable Voting Tools'), t('Provide a class of post which others can vote on'),false), - array('delayed_posting', t('Delayed Posting'), t('Allow posts to be published at a later date'),false), - array('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), +// array('richtext', t('Richtext Editor'), t('Enable richtext editor'),falseget_config('feature_lock','richtext')), +// array('markdown', t('Use Markdown'), t('Allow use of "Markdown" to format posts'),false,get_config('feature_lock','markdown')), + array('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')), + array('channel_sources', t('Channel Sources'), t('Automatically import channel content from other channels or feeds'),false,get_config('feature_lock','channel_sources')), + array('content_encrypt', t('Even More Encryption'), t('Allow optional encryption of content end-to-end with a shared secret key'),false,get_config('feature_lock','content_encrypt')), + array('consensus_tools', t('Enable Voting Tools'), t('Provide a class of post which others can vote on'),false,get_config('feature_lock','consensus_tools')), + array('delayed_posting', t('Delayed Posting'), t('Allow posts to be published at a later date'),false,get_config('feature_lock','delayed_posting')), + array('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')), ), // Network Tools 'net_module' => array( t('Network and Stream Filtering'), - array('archives', t('Search by Date'), t('Ability to select posts by date ranges'),false), - array('groups', t('Collections Filter'), t('Enable widget to display Network posts only from selected collections'),false), - array('savedsearch', t('Saved Searches'), t('Save search terms for re-use'),false), - array('personal_tab', t('Network Personal Tab'), t('Enable tab to display only Network posts that you\'ve interacted on'),false), - array('new_tab', t('Network New Tab'), t('Enable tab to display all new Network activity'),false), - array('affinity', t('Affinity Tool'), t('Filter stream activity by depth of relationships'),false), - array('connfilter', t('Connection Filtering'), t('Filter incoming posts from connections based on keywords/content')), - array('suggest', t('Suggest Channels'), t('Show channel suggestions'),false), + array('archives', t('Search by Date'), t('Ability to select posts by date ranges'),false,get_config('feature_lock','archives')), + array('groups', t('Privacy Groups'), t('Enable management and selection of privacy groups'),true,get_config('feature_lock','groups')), + array('savedsearch', t('Saved Searches'), t('Save search terms for re-use'),false,get_config('feature_lock','savedsearch')), + array('personal_tab', t('Network Personal Tab'), t('Enable tab to display only Network posts that you\'ve interacted on'),false,get_config('feature_lock','personal_tab')), + array('new_tab', t('Network New Tab'), t('Enable tab to display all new Network activity'),false,get_config('feature_lock','new_tab')), + array('affinity', t('Affinity Tool'), t('Filter stream activity by depth of relationships'),false,get_config('feature_lock','affinity')), + array('connfilter', t('Connection Filtering'), t('Filter incoming posts from connections based on keywords/content'),false,get_config('feature_lock','connfilter')), + array('suggest', t('Suggest Channels'), t('Show channel suggestions'),false,get_config('feature_lock','suggest')), ), // Item tools 'tools' => array( t('Post/Comment Tools'), - array('commtag', t('Tagging'), t('Ability to tag existing posts'),false), - array('categories', t('Post Categories'), t('Add categories to your posts'),false), - array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false), - array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false), - array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false), - array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page'),false), + array('commtag', t('Community Tagging'), t('Ability to tag existing posts'),false,get_config('feature_lock','commtag')), + array('categories', t('Post Categories'), t('Add categories to your posts'),false,get_config('feature_lock','categories')), + array('filing', t('Saved Folders'), t('Ability to file posts under folders'),false,get_config('feature_lock','filing')), + array('dislike', t('Dislike Posts'), t('Ability to dislike posts/comments'),false,get_config('feature_lock','dislike')), + array('star_posts', t('Star Posts'), t('Ability to mark special posts with a star indicator'),false,get_config('feature_lock','star_posts')), + array('tagadelic', t('Tag Cloud'), t('Provide a personal tag cloud on your channel page'),false,get_config('feature_lock','tagedelic')), ), ); + // removed any locked features and remove the entire category if this makes it empty + + if($filtered) { + foreach($arr as $k => $x) { + $has_items = false; + for($y = 0; $y < count($arr[$k]); $y ++) { + if(is_array($arr[$k][$y])) { + if($arr[$k][$y][4] === false) { + $has_items = true; + } + else { + unset($arr[$k][$y]); + } + } + } + if(! $has_items) { + unset($arr[$k]); + } + } + } + call_hooks('get_features',$arr); return $arr; } diff --git a/include/follow.php b/include/follow.php index 97be82da7..5e1146657 100644 --- a/include/follow.php +++ b/include/follow.php @@ -122,6 +122,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) else $permissions = $j['permissions']; + foreach($permissions as $k => $v) { if($v) { $their_perms = $their_perms | intval($global_perms[$k][1]); @@ -167,6 +168,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) } } + if(! $xchan_hash) { $result['message'] = t('Channel discovery failed.'); logger('follow: ' . $result['message']); @@ -202,6 +204,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) $default_group = $r[0]['channel_default_group']; } + if($is_http) { @@ -226,6 +229,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) dbesc($xchan_hash), intval($uid) ); + + if($r) { $abook_instance = $r[0]['abook_instance']; @@ -242,7 +247,6 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) ); } else { - $closeness = get_pconfig($uid,'system','new_abook_closeness'); if($closeness === false) $closeness = 80; @@ -270,6 +274,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false) dbesc($xchan_hash), intval($uid) ); + if($r) { $result['abook'] = $r[0]; proc_run('php', 'include/notifier.php', 'permission_create', $result['abook']['abook_id']); diff --git a/include/group.php b/include/group.php index 0875b10f9..22f221059 100644 --- a/include/group.php +++ b/include/group.php @@ -200,7 +200,7 @@ function group_get_members($gid) { if(intval($gid)) { $r = q("SELECT * FROM `group_member` LEFT JOIN abook ON abook_xchan = `group_member`.`xchan` left join xchan on xchan_hash = abook_xchan - WHERE `gid` = %d AND abook_channel = %d and `group_member`.`uid` = %d and xchan_deleted = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", + WHERE `gid` = %d AND abook_channel = %d and `group_member`.`uid` = %d and xchan_deleted = 0 and abook_self = 0 and abook_blocked = 0 and abook_pending = 0 ORDER BY xchan_name ASC ", intval($gid), intval(local_channel()), intval(local_channel()) @@ -229,7 +229,7 @@ function mini_group_select($uid,$group = '') { logger('mini_group_select: ' . print_r($grps,true), LOGGER_DATA); $o = replace_macros(get_markup_template('group_selection.tpl'), array( - '$label' => t('Add new connections to this collection (privacy group)'), + '$label' => t('Add new connections to this privacy group'), '$groups' => $grps )); return $o; @@ -292,10 +292,10 @@ function group_side($every="connections",$each="group",$edit = false, $group_id $tpl = get_markup_template("group_side.tpl"); $o = replace_macros($tpl, array( - '$title' => t('Collections'), - '$edittext' => t('Edit collection'), - '$createtext' => t('Add new collection'), - '$ungrouped' => (($every === 'contacts') ? t('Channels not in any collection') : ''), + '$title' => t('Privacy Groups'), + '$edittext' => t('Edit group'), + '$createtext' => t('Add privacy group'), + '$ungrouped' => (($every === 'contacts') ? t('Channels not in any privacy group') : ''), '$groups' => $groups, '$add' => t('add'), )); diff --git a/include/identity.php b/include/identity.php index 98ba26bd8..382b096fe 100644 --- a/include/identity.php +++ b/include/identity.php @@ -178,7 +178,7 @@ function create_identity($arr) { $ret = array('success' => false); if(! $arr['account_id']) { - $ret['message'] = t('No account identifier'); + $ret['message'] = t('No account identifier'); return $ret; } $ret = identity_check_service_class($arr['account_id']); @@ -352,7 +352,7 @@ function create_identity($arr) { ); if($role_permissions) { - $myperms = ((array_key_exists('perms_auto',$role_permissions) && $role_permissions['perms_auto']) ? intval($role_permissions['perms_accept']) : 0); + $myperms = ((array_key_exists('perms_accept',$role_permissions)) ? intval($role_permissions['perms_accept']) : 0); } else $myperms = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_PHOTOS|PERMS_R_ABOOK @@ -482,7 +482,9 @@ function identity_basic_export($channel_id, $items = false) { $ret = array(); - $ret['compatibility'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_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'] = array('project' => PLATFORM_NAME, 'version' => RED_VERSION, 'database' => DB_UPDATE_VERSION, 'server_role' => Zotlabs\Project\System::get_server_role()); $r = q("select * from channel where channel_id = %d limit 1", intval($channel_id) @@ -503,8 +505,12 @@ function identity_basic_export($channel_id, $items = false) { if($r) { $ret['abook'] = $r; - foreach($r as $rr) - $xchans[] = $rr['abook_xchan']; + for($x = 0; $x < count($ret['abook']); $x ++) { + $xchans[] = $ret['abook'][$x]['abook_chan']; + $abconfig = load_abconfig($ret['channel']['channel_hash'],$ret['abook'][$x]['abook_xchan']); + if($abconfig) + $ret['abook'][$x]['abconfig'] = $abconfig; + } stringify_array_elms($xchans); } @@ -898,6 +904,55 @@ function profile_load(&$a, $nickname, $profile = '') { } +function profile_edit_menu($uid) { + + $a = get_app(); + $ret = array(); + + $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' => array(), + ); + + $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); + if($multi_profiles) { + $ret['multi'] = 1; + $ret['edit'] = array($a->get_baseurl(). '/profiles', t('Edit Profiles'), '', t('Edit')); + $ret['menu']['cr_new'] = t('Create New Profile'); + } + else { + $ret['edit'] = array($a->get_baseurl() . '/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'][] = array( + '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. * @@ -911,7 +966,7 @@ function profile_load(&$a, $nickname, $profile = '') { * @return HTML string suitable for sidebar inclusion * Exceptions: Returns empty string if passed $profile is wrong type or not populated */ -function profile_sidebar($profile, $block = 0, $show_connect = true) { +function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = false) { $a = get_app(); @@ -922,17 +977,18 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $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']); - $is_owner = (($profile['uid'] == local_channel()) ? true : false); - if(is_sys_channel($profile['uid'])) $show_connect = false; - $profile['picdate'] = urlencode($profile['picdate']); call_hooks('profile_sidebar_enter', $profile); @@ -954,42 +1010,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $connect_url = z_root() . '/connect/' . $profile['channel_address']; } - // show edit profile to yourself - if($is_owner) { - $profile['menu'] = array( - 'chg_photo' => t('Change profile photo'), - 'entries' => array(), - ); - - $multi_profiles = feature_enabled(local_channel(), 'multi_profiles'); - if($multi_profiles) { - $profile['edit'] = array($a->get_baseurl(). '/profiles', t('Profiles'),"", t('Manage/edit profiles')); - $profile['menu']['cr_new'] = t('Create New Profile'); - } - else - $profile['edit'] = array($a->get_baseurl() . '/profiles/' . $profile['id'], t('Edit Profile'),'',t('Edit Profile')); - - $r = q("SELECT * FROM `profile` WHERE `uid` = %d", - local_channel()); - - if($r) { - foreach($r as $rr) { - if(!($multi_profiles || $rr['is_default'])) - continue; - $profile['menu']['entries'][] = array( - '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'), - ); - } - } - } - - if((x($profile,'address') == 1) || (x($profile,'locality') == 1) || (x($profile,'region') == 1) @@ -1006,9 +1026,6 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { // logger('online: ' . $profile['online']); - if(! perm_is_allowed($profile['uid'],((is_array($observer)) ? $observer['xchan_hash'] : ''),'view_profile')) { - $block = true; - } if(($profile['hidewall'] && (! local_channel()) && (! remote_channel())) || $block ) { $location = $reddress = $pdesc = $gender = $marital = $homepage = False; @@ -1046,12 +1063,18 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { $channel_menu .= comanche_block($menublock); } - $tpl = get_markup_template('profile_vcard.tpl'); + if($zcard) + $tpl = get_markup_template('profile_vcard_short.tpl'); + else + $tpl = get_markup_template('profile_vcard.tpl'); require_once('include/widgets.php'); - $z = widget_rating(array('target' => $profile['channel_hash'])); + + if(! feature_enabled($profile['uid'],'hide_rating')) + $z = widget_rating(array('target' => $profile['channel_hash'])); $o .= replace_macros($tpl, array( + '$zcard' => $zcard, '$profile' => $profile, '$connect' => $connect, '$connect_url' => $connect_url, @@ -1065,6 +1088,7 @@ function profile_sidebar($profile, $block = 0, $show_connect = true) { '$reddress' => $reddress, '$rating' => $z, '$contact_block' => $contact_block, + '$editmenu' => profile_edit_menu($profile['uid']) )); $arr = array('profile' => &$profile, 'entry' => &$o); @@ -1227,11 +1251,25 @@ function advanced_profile(&$a) { if(! perm_is_allowed($a->profile['profile_uid'],get_observer_hash(),'view_profile')) return ''; - $o = ''; + if($a->profile['name']) { + + $profile_fields_basic = get_profile_fields_basic(); + $profile_fields_advanced = get_profile_fields_advanced(); + + $advanced = ((feature_enabled($a->profile['profile_uid'],'advanced_profiles')) ? true : false); + if($advanced) + $fields = $profile_fields_advanced; + else + $fields = $profile_fields_basic; + + $clean_fields = array(); + if($fields) { + foreach($fields as $k => $v) { + $clean_fields[] = trim($k); + } + } - $o .= '<h2>' . t('Profile') . '</h2>'; - if($a->profile['name']) { $tpl = get_markup_template('profile_advanced.tpl'); @@ -1258,7 +1296,7 @@ function advanced_profile(&$a) { $profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun'); if($likers) { foreach($likers as $l) - $profile['likers'][] = array('name' => $l['xchan_name'],'url' => zid($l['xchan_url'])); + $profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url'])); } if(($a->profile['dob']) && ($a->profile['dob'] != '0000-00-00')) { @@ -1350,6 +1388,8 @@ function advanced_profile(&$a) { '$canlike' => (($profile['canlike'])? true : false), '$likethis' => t('Like this thing'), '$profile' => $profile, + '$fields' => $clean_fields, + '$editmenu' => profile_edit_menu($a->profile['profile_uid']), '$things' => $things )); } @@ -1695,3 +1735,145 @@ function profiles_build_sync($channel_id) { build_sync_packet($channel_id,array('profile' => $r)); } } + + +function auto_channel_create($account_id) { + + if(! $account_id) + return false; + + $arr = array(); + $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'); + + 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); + + $arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000,9999))); + + return create_identity($arr); + +} + +function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_1200) { + + $r = q("select height, width, resource_id, type from photo where uid = %d and scale = %d and photo_usage = %d", + intval($channel_id), + intval($res), + intval(PHOTO_COVER) + ); + if(! $r) + return false; + + $output = false; + + $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 = '<img class="zrl" width="' . $r[0]['width'] . '" height="' . $r[0]['height'] . '" src="' . $url . '" alt="' . t('cover photo') . '" />'; + break; + case 'array': + default: + $output = array( + 'width' => $r[0]['width'], + 'height' => $r[0]['type'], + 'type' => $r[0]['type'], + 'url' => $url + ); + break; + } + + return $output; + +} + +function get_zcard($channel,$observer_hash = '',$args = array()) { + + logger('get_zcard'); + + $maxwidth = (($args['width']) ? intval($args['width']) : 0); + $maxheight = (($args['height']) ? intval($args['height']) : 0); + + + if(($maxwidth > 1200) || ($maxwidth < 1)) + $maxwidth = 1200; + + if($maxwidth <= 425) { + $width = 425; + $size = 'hz_small'; + $cover_size = PHOTO_RES_COVER_425; + $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']); + } + elseif($maxwidth <= 900) { + $width = 900; + $size = 'hz_medium'; + $cover_size = PHOTO_RES_COVER_850; + $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']); + } + elseif($maxwidth <= 1200) { + $width = 1200; + $size = 'hz_large'; + $cover_size = PHOTO_RES_COVER_1200; + $pphoto = array('type' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']); + } + +// $scale = (float) $maxwidth / $width; +// $translate = intval(($scale / 1.0) * 100); + + + $channel['channel_addr'] = $channel['channel_address'] . '@' . get_app()->get_hostname(); + $zcard = array('chan' => $channel); + + $r = q("select height, width, resource_id, scale, type from photo where uid = %d and scale = %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]['scale']; + } + else { + $cover = $pphoto; + } + + $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; + +} diff --git a/include/import.php b/include/import.php index ffaea6c1a..b7ec1c565 100644 --- a/include/import.php +++ b/include/import.php @@ -2,7 +2,7 @@ require_once('include/menu.php'); -function import_channel($channel, $account_id) { +function import_channel($channel, $account_id, $seize) { if(! array_key_exists('channel_system',$channel)) { $channel['channel_system'] = (($channel['channel_pageflags'] & 0x1000) ? 1 : 0); @@ -496,6 +496,8 @@ function import_items($channel,$items) { } } + $deliver = false; // Don't deliver any messages or notifications when importing + foreach($items as $i) { $item = get_item_elements($i,$allow_code); if(! $item) @@ -509,16 +511,15 @@ function import_items($channel,$items) { if($item['edited'] > $r[0]['edited']) { $item['id'] = $r[0]['id']; $item['uid'] = $channel['channel_id']; - item_store_update($item); + item_store_update($item,$allow_code,$deliver); continue; } } else { $item['aid'] = $channel['channel_account_id']; $item['uid'] = $channel['channel_id']; - $item_result = item_store($item); + $item_result = item_store($item,$allow_code,$deliver); } - } } } diff --git a/include/items.php b/include/items.php index 44f9633a9..bb4d1108e 100755 --- a/include/items.php +++ b/include/items.php @@ -159,7 +159,7 @@ function filter_insecure($channel_id, $arr) { $ret = array(); - if((! intval(get_pconfig($channel_id, 'system', 'filter_insecure_collections'))) || (! $arr)) + if((! intval(get_pconfig($channel_id, 'system', 'filter_insecure_privacy_groups'))) || (! $arr)) return $arr; $str = ''; @@ -552,6 +552,12 @@ function get_public_feed($channel, $params) { $params['top'] = ((x($params,'top')) ? intval($params['top']) : 0); $params['cat'] = ((x($params,'cat')) ? $params['cat'] : ''); + + // put a sane lower limit on feed requests if not specified + + if($params['begin'] === NULL_DATE) + $params['begin'] = datetime_convert('UTC','UTC','now - 1 month'); + switch($params['type']) { case 'json': header("Content-type: application/atom+json"); @@ -587,8 +593,8 @@ function get_feed_for($channel, $observer_hash, $params) { } $items = items_fetch(array( 'wall' => '1', - 'datequery' => $params['begin'], - 'datequery2' => $params['end'], + 'datequery' => $params['end'], + 'datequery2' => $params['begin'], 'start' => $params['start'], // FIXME 'records' => $params['records'], // FIXME 'direction' => $params['direction'], // FIXME @@ -604,8 +610,8 @@ function get_feed_for($channel, $observer_hash, $params) { $atom = ''; $atom .= replace_macros($feed_template, array( - '$version' => xmlify(RED_VERSION), - '$red' => xmlify(PLATFORM_NAME), + '$version' => xmlify(Zotlabs\Project\System::get_project_version()), + '$red' => xmlify(Zotlabs\Project\System::get_platform_name()), '$feed_id' => xmlify($channel['xchan_url']), '$feed_title' => xmlify($channel['channel_name']), '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) , @@ -623,6 +629,7 @@ function get_feed_for($channel, $observer_hash, $params) { '$community' => '', )); + call_hooks('atom_feed', $atom); if($items) { @@ -842,7 +849,7 @@ function get_item_elements($x,$allow_code = false) { if($allow_code) $arr['body'] = $x['body']; else - $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : ''); + $arr['body'] = (($x['body']) ? htmlspecialchars($x['body'],ENT_COMPAT,'UTF-8',false) : ''); $key = get_config('system','pubkey'); @@ -910,6 +917,7 @@ function get_item_elements($x,$allow_code = false) { $arr['attach'] = activity_sanitise($x['attach']); $arr['term'] = decode_tags($x['tags']); + $arr['iconfig'] = decode_item_meta($x['meta']); $arr['item_private'] = ((array_key_exists('flags',$x) && is_array($x['flags']) && in_array('private',$x['flags'])) ? 1 : 0); @@ -1317,6 +1325,9 @@ function encode_item($item,$mirror = false) { if($item['term']) $x['tags'] = encode_item_terms($item['term'],$mirror); + if($item['iconfig']) + $x['meta'] = encode_item_meta($item['iconfig'],$mirror); + if($item['diaspora_meta']) { $z = json_decode($item['diaspora_meta'],true); if($z) { @@ -1427,6 +1438,30 @@ function encode_item_terms($terms,$mirror = false) { return $ret; } +function encode_item_meta($meta,$mirror = false) { + $ret = array(); + + if($meta) { + foreach($meta as $m) { + if($m['sharing'] || $mirror) + $ret[] = array('family' => $m['cat'], 'key' => $m['k'], 'value' => $m['v'], 'sharing' => intval($m['sharing'])); + } + } + + return $ret; +} + +function decode_item_meta($meta) { + $ret = array(); + + if(is_array($meta) && $meta) { + foreach($meta as $m) { + $ret[] = array('cat' => escape_tags($m['family']),'k' => escape_tags($m['key']),'v' => $m['value'],'sharing' => $m['sharing']); + } + } + return $ret; +} + /** * @brief * @@ -2160,7 +2195,7 @@ function encode_rel_links($links) { * * \e boolean \b success * * \e int \b item_id */ -function item_store($arr, $allow_exec = false) { +function item_store($arr, $allow_exec = false, $deliver = true) { $d = array('item' => $arr, 'allow_exec' => $allow_exec); call_hooks('item_store', $d ); @@ -2439,6 +2474,13 @@ function item_store($arr, $allow_exec = false) { unset($arr['term']); } + $meta = null; + if(array_key_exists('iconfig',$arr)) { + $meta = $arr['iconfig']; + unset($arr['iconfig']); + } + + if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid) || strlen($public_policy)) $private = 1; else @@ -2516,6 +2558,15 @@ function item_store($arr, $allow_exec = false) { $arr['term'] = $terms; } + if($meta) { + foreach($meta as $m) { + set_iconfig($current_post,$m['cat'],$m['k'],$m['v'],$m['sharing']); + } + $arr['iconfig'] = $meta; + } + + + call_hooks('post_remote_end',$arr); // update the commented timestamp on the parent @@ -2537,7 +2588,7 @@ function item_store($arr, $allow_exec = false) { // so that we have an item in the DB that's marked deleted and won't store a fresh post // that isn't aware that we were already told to delete it. - if(! intval($arr['item_deleted'])) { + if(($deliver) && (! intval($arr['item_deleted']))) { send_status_notifications($current_post,$arr); tag_deliver($arr['uid'],$current_post); } @@ -2550,7 +2601,7 @@ function item_store($arr, $allow_exec = false) { -function item_store_update($arr,$allow_exec = false) { +function item_store_update($arr,$allow_exec = false, $deliver = true) { $d = array('item' => $arr, 'allow_exec' => $allow_exec); call_hooks('item_store_update', $d ); @@ -2737,6 +2788,13 @@ function item_store_update($arr,$allow_exec = false) { unset($arr['term']); } + $meta = null; + if(array_key_exists('iconfig',$arr)) { + $meta = $arr['iconfig']; + unset($arr['iconfig']); + } + + dbesc_array($arr); logger('item_store_update: ' . print_r($arr,true), LOGGER_DATA); @@ -2778,11 +2836,24 @@ function item_store_update($arr,$allow_exec = false) { $arr['term'] = $terms; } + $r = q("delete from iconfig where iid = %d", + intval($orig_post_id) + ); + + if($meta) { + foreach($meta as $m) { + set_iconfig($orig_post_id,$m['cat'],$m['k'],$m['v'],$m['sharing']); + } + $arr['iconfig'] = $meta; + } + call_hooks('post_remote_update_end',$arr); - send_status_notifications($orig_post_id,$arr); + if($deliver) { + send_status_notifications($orig_post_id,$arr); + tag_deliver($uid,$orig_post_id); + } - tag_deliver($uid,$orig_post_id); $ret['success'] = true; $ret['item_id'] = $orig_post_id; @@ -2816,13 +2887,13 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, $signed_text = $datarray['mid'] . ';' . $parent_item['mid'] . ';' . $signed_body . ';' . $diaspora_handle; - /** @FIXME $uprvkey is undefined, do we still need this if-statement? */ - if( $uprvkey !== false ) + + if( $channel && $channel['channel_prvkey'] ) $authorsig = base64_encode(rsa_sign($signed_text, $channel['channel_prvkey'], 'sha256')); else $authorsig = ''; - $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => base64_encode($authorsig)); + $x = array('signer' => $diaspora_handle, 'body' => $signed_body, 'signed_text' => $signed_text, 'signature' => $authorsig); $y = json_encode($x); @@ -2831,6 +2902,7 @@ function store_diaspora_comment_sig($datarray, $channel, $parent_item, $post_id, intval($post_id) ); + if(! $r) logger('store_diaspora_comment_sig: DB write failed'); @@ -3436,6 +3508,7 @@ function check_item_source($uid, $item) { if(! $r[0]['src_patt']) return true; + require_once('include/html2plain.php'); $text = prepare_text($item['body'],$item['mimetype']); $text = html2plain($text); @@ -3651,10 +3724,6 @@ function mail_store($arr) { /** * @brief Process atom feed and update anything/everything we might need to update. * - * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or - * might not) try and subscribe to it. - * $datedir sorts in reverse order - * * @param array $xml * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. * @param $importer @@ -3685,6 +3754,16 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { return; } + $sys_expire = intval(get_config('system','default_expire_days')); + $chn_expire = intval($importer['channel_expire_days']); + + $expire_days = $sys_expire; + + if(($chn_expire != 0) && ($chn_expire < $sys_expire)) + $expire_days = $chn_expire; + + // logger('expire_days: ' . $expire_days); + $feed = new SimplePie(); $feed->set_raw_data($xml); $feed->init(); @@ -3788,6 +3867,7 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { intval($importer['channel_id']) ); + // Update content if 'updated' changes if($r) { @@ -3846,6 +3926,17 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { $datarray['owner_xchan'] = $contact['xchan_hash']; + if(array_key_exists('created',$datarray) && $datarray['created'] != NULL_DATE && $expire_days) { + $t1 = $datarray['created']; + $t2 = datetime_convert('UTC','UTC','now - ' . $expire_days . 'days'); + if($t1 < $t2) { + logger('feed content older than expiration. Ignoring.', LOGGER_DEBUG, LOG_INFO); + continue; + } + } + + + $r = q("SELECT edited FROM item WHERE mid = '%s' AND uid = %d LIMIT 1", dbesc($item_id), intval($importer['channel_id']) @@ -3892,6 +3983,72 @@ function consume_feed($xml, $importer, &$contact, $pass = 0) { } } + +/** + * @brief Process atom feed and return the first post and structure + * + * @param array $xml + * The (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds. + * @param $importer + * The contact_record (joined to user_record) of the local user who owns this + * relationship. It is this person's stuff that is going to be updated. + */ + +function process_salmon_feed($xml, $importer) { + + $ret = array(); + + require_once('library/simplepie/simplepie.inc'); + + if(! strlen($xml)) { + logger('process_feed: empty input'); + return; + } + + $feed = new SimplePie(); + $feed->set_raw_data($xml); + $feed->init(); + + if($feed->error()) + logger('Error parsing XML: ' . $feed->error()); + + $permalink = $feed->get_permalink(); + + if($feed->get_item_quantity()) { + + // this should be exactly one + + logger('feed item count = ' . $feed->get_item_quantity(), LOGGER_DEBUG); + + $items = $feed->get_items(); + + foreach($items as $item) { + + $item_id = base64url_encode($item->get_id()); + + logger('processing ' . $item_id, LOGGER_DEBUG); + + $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to'); + if(isset($rawthread[0]['attribs']['']['ref'])) { + $is_reply = true; + $parent_mid = base64url_encode($rawthread[0]['attribs']['']['ref']); + } + + if($is_reply) + $ret['is_reply'] = true; + + $ret['author'] = array(); + + $datarray = get_atom_elements($feed,$item,$ret['author']); + + $ret['item'] = $datarray; + } + } + + return $ret; +} + + function update_feed_item($uid,$datarray) { logger('update_feed_item: not implemented! ' . $uid . ' ' . print_r($datarray,true), LOGGER_DATA); } @@ -3975,12 +4132,25 @@ function atom_entry($item,$type,$author,$owner,$comment = false,$cid = 0) { $o .= '<thr:in-reply-to ref="' . xmlify($parent_item) . '" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; } + if(activity_match($item['obj_type'],ACTIVITY_OBJ_EVENT) && activity_match($item['verb'],ACTIVITY_POST)) { + $obj = ((is_array($item['obj'])) ? $item['object'] : json_decode($item['object'],true)); + + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<summary>' . xmlify(bbcode($obj['title'])) . '</summary>' . "\r\n"; + $o .= '<dtstart xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['start'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtstart>' . "\r\n"; + $o .= '<dtend xmlns="urn:ietf:params:xml:ns:xcal">' . datetime_convert('UTC','UTC', $obj['finish'],'Ymd\\THis' . (($obj['adjust']) ? '\\Z' : '')) . '</dtend>' . "\r\n"; + $o .= '<location>' . bbcode($obj['location']) . '</location>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(bbcode($obj['description'])) . '</content>' . "\r\n"; + } + else { + $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; + $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; + } + $o .= '<id>' . xmlify($item['mid']) . '</id>' . "\r\n"; - $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; $o .= '<published>' . xmlify(datetime_convert('UTC','UTC',$item['created'] . '+00:00',ATOM_TIME)) . '</published>' . "\r\n"; $o .= '<updated>' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '</updated>' . "\r\n"; - $o .= '<content type="' . $type . '" >' . xmlify(prepare_text($body,$item['mimetype'])) . '</content>' . "\r\n"; $o .= '<link rel="alternate" type="text/html" href="' . xmlify($item['plink']) . '" />' . "\r\n"; if($item['location']) { @@ -4643,6 +4813,10 @@ function fetch_post_tags($items,$link = false) { dbesc($tag_finder_str), intval(TERM_OBJ_POST) ); + $imeta = q("select * from iconfig where iid in ( %s )", + dbesc($tag_finder_str) + ); + } for($x = 0; $x < count($items); $x ++) { @@ -4666,6 +4840,26 @@ function fetch_post_tags($items,$link = false) { } } } + if($imeta) { + foreach($imeta as $i) { + if(array_key_exists('item_id',$items[$x])) { + if($i['iid'] == $items[$x]['item_id']) { + if(! is_array($items[$x]['iconfig'])) + $items[$x]['iconfig'] = array(); + $i['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$i['v'])) ? unserialize($i['v']) : $i['v']); + $items[$x]['iconfig'][] = $i; + } + } + else { + if($i['iid'] == $items[$x]['id']) { + if(! is_array($items[$x]['iconfig'])) + $items[$x]['iconfig'] = array(); + $i['v'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$i['v'])) ? unserialize($i['v']) : $i['v']); + $items[$x]['iconfig'][] = $i; + } + } + } + } } return $items; @@ -4835,7 +5029,7 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C intval($uid) ); if(! $r) { - $result['message'] = t('Collection not found.'); + $result['message'] = t('Privacy group not found.'); return $result; } @@ -4851,14 +5045,14 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } } else { $contact_str = ' 0 '; - $result['message'] = t('Collection is empty.'); + $result['message'] = t('Privacy group is empty.'); return $result; } $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($r[0]['hash']) . '>%') . "' ) and id = parent $item_normal ) "; $x = group_rec_byhash($uid,$r[0]['hash']); - $result['headline'] = sprintf( t('Collection: %s'),$x['name']); + $result['headline'] = sprintf( t('Privacy group: %s'),$x['name']); } elseif($arr['cid'] && $uid) { @@ -4876,15 +5070,15 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C } if ($arr['datequery']) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery'])))); + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created <= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery'])))); } if ($arr['datequery2']) { - $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert(date_default_timezone_get(),'',$arr['datequery2'])))); + $sql_extra3 .= protect_sprintf(sprintf(" AND item.created >= '%s' ", dbesc(datetime_convert('UTC','UTC',$arr['datequery2'])))); } if(! array_key_exists('nouveau',$arr)) { $sql_extra2 = " AND item.parent = item.id "; - $sql_extra3 = ''; +// $sql_extra3 = ''; } if($arr['search']) { @@ -5274,3 +5468,212 @@ function asencode_person($p) { return $ret; } + + +function send_profile_photo_activity($channel,$photo,$profile) { + + // for now only create activities for the default profile + + if(! intval($profile['is_default'])) + return; + + $arr = array(); + $arr['item_thread_top'] = 1; + $arr['item_origin'] = 1; + $arr['item_wall'] = 1; + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['verb'] = ACTIVITY_UPDATE; + + $arr['object'] = json_encode(array( + 'type' => $arr['obj_type'], + 'id' => z_root() . '/photo/profile/l/' . $channel['channel_id'], + 'link' => array('rel' => 'photo', 'type' => $photo['type'], 'href' => z_root() . '/photo/profile/l/' . $channel['channel_id']) + )); + + if(stripos($profile['gender'],t('female')) !== false) + $t = t('%1$s updated her %2$s'); + elseif(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('profile photo') . '[/zrl]'; + + $ltext = '[zrl=' . z_root() . '/profile/' . $channel['channel_address'] . ']' . '[zmg=150x150]' . z_root() . '/photo/' . $photo['resource_id'] . '-4[/zmg][/zrl]'; + + $arr['body'] = sprintf($t,$channel['channel_name'],$ptext) . "\n\n" . $ltext; + + $acl = new Zotlabs\Access\AccessList($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); + + +} + + + + + +function get_iconfig(&$item, $family, $key) { + + $is_item = false; + if(is_array($item)) { + $is_item = true; + if((! array_key_exists('iconfig',$item)) || (! is_array($item['iconfig']))) + $item['iconfig'] = array(); + + if(array_key_exists('item_id',$item)) + $iid = $item['item_id']; + else + $iid = $item['id']; + } + elseif(intval($item)) + $iid = $item; + + if(! $iid) + return false; + + if(is_array($item) && array_key_exists('iconfig',$item) && is_array($item['iconfig'])) { + foreach($item['iconfig'] as $c) { + if($c['iid'] == $iid && $c['cat'] == $family && $c['k'] == $key) + return $c['v']; + } + } + + $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'] = ((preg_match('|^a:[0-9]+:{.*}$|s',$r[0]['v'])) ? unserialize($r[0]['v']) : $r[0]['v']); + if($is_item) + $item['iconfig'][] = $r[0]; + return $r[0]['v']; + } + return false; + +} + +/** + * set_iconfig(&$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. + */ + + +function set_iconfig(&$item, $family, $key, $value, $sharing = false) { + + $dbvalue = ((is_array($value)) ? serialize($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'] = array(); + 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(get_iconfig($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; +} + + + +function del_iconfig(&$item, $family, $key) { + + + $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]); + } + } + } + return true; + } + + if(intval($item)) + $iid = intval($item); + + if(! $iid) + return false; + + return q("delete from iconfig where iid = %d and cat = '%s' and k = '%s' ", + intval($iid), + dbesc($family), + dbesc($key) + ); + +} + diff --git a/include/language.php b/include/language.php index 59979aa85..121816ae6 100644 --- a/include/language.php +++ b/include/language.php @@ -65,15 +65,22 @@ function get_best_language() { if(isset($langs) && count($langs)) { foreach ($langs as $lang => $v) { $lang = strtolower($lang); - if(file_exists("view/$lang") && is_dir("view/$lang")) { + if(is_dir("view/$lang")) { $preferred = $lang; break; } } } - if(isset($preferred)) - return $preferred; + if(! isset($preferred)) + $preferred = 'unset'; + + $arr = array('langs' => $langs, 'preferred' => $preferred); + + call_hooks('get_best_language',$arr); + + if($arr['preferred'] !== 'unset') + return $arr['preferred']; $a = get_app(); return ((isset($a->config['system']['language'])) ? $a->config['system']['language'] : 'en'); @@ -182,7 +189,7 @@ function t($s, $ctx = '') { function translate_projectname($s) { - return str_replace(array('$projectname','$Projectname'),array(PLATFORM_NAME,ucfirst(PLATFORM_NAME)),$s); + return str_replace(array('$projectname','$Projectname'),array(Zotlabs\Project\System::get_platform_name(),ucfirst(Zotlabs\Project\System::get_platform_name())),$s); } diff --git a/include/menu.php b/include/menu.php index 075372515..d90cefddb 100644 --- a/include/menu.php +++ b/include/menu.php @@ -299,7 +299,7 @@ function menu_add_item($menu_id, $uid, $arr) { $channel = get_app()->get_channel(); } - $acl = new AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); $acl->set_from_array($arr); $p = $acl->get(); @@ -340,7 +340,7 @@ function menu_edit_item($menu_id, $uid, $arr) { $channel = get_app()->get_channel(); } - $acl = new AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); $acl->set_from_array($arr); $p = $acl->get(); diff --git a/include/message.php b/include/message.php index 940fcc275..f24bb6e4b 100644 --- a/include/message.php +++ b/include/message.php @@ -11,6 +11,7 @@ require_once('include/attach.php'); function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='',$expires = ''){ $ret = array('success' => false); + $is_reply = false; $a = get_app(); $observer_hash = get_observer_hash(); @@ -51,6 +52,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' $conv_guid = ''; if(strlen($replyto)) { + $is_reply = true; $r = q("select conv_guid from mail where channel_id = %d and ( mid = '%s' or parent_mid = '%s' ) limit 1", intval(local_channel()), dbesc($replyto), @@ -187,8 +189,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' - $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires ) - VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", + $r = q("INSERT INTO mail ( account_id, conv_guid, mail_obscured, channel_id, from_xchan, to_xchan, title, body, attach, mid, parent_mid, created, expires, mail_isreply ) + VALUES ( %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d )", intval($channel['channel_account_id']), dbesc($conv_guid), intval(1), @@ -201,7 +203,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto=' dbesc($mid), dbesc($replyto), dbesc(datetime_convert()), - dbescdate($expires) + dbescdate($expires), + intval($is_reply) ); // verify the save diff --git a/include/nav.php b/include/nav.php index 1630c4731..5f630745c 100644 --- a/include/nav.php +++ b/include/nav.php @@ -76,7 +76,7 @@ EOT; if(local_channel()) { - if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select')) + if($chans && count($chans) > 1 && feature_enabled(local_channel(),'nav_channel_select') && (! UNO)) $nav['channels'] = $chans; $nav['logout'] = Array('logout',t('Logout'), "", t('End this session'),'logout_nav_btn'); @@ -84,7 +84,7 @@ EOT; // user menu $nav['usermenu'][] = Array('channel/' . $channel['channel_address'], t('Home'), "", t('Your posts and conversations'),'channel_nav_btn'); $nav['usermenu'][] = Array('profile/' . $channel['channel_address'], t('View Profile'), "", t('Your profile page'),'profile_nav_btn'); - if(feature_enabled(local_channel(),'multi_profiles')) + if(feature_enabled(local_channel(),'multi_profiles') && (! UNO)) $nav['usermenu'][] = Array('profiles', t('Edit Profiles'),"", t('Manage/Edit profiles'),'profiles_nav_btn'); else $nav['usermenu'][] = Array('profiles/' . $prof[0]['id'], t('Edit Profile'),"", t('Edit your profile'),'profiles_nav_btn'); @@ -94,16 +94,17 @@ EOT; require_once('include/chat.php'); $has_chats = chatroom_list_count(local_channel()); - $nav['usermenu'][] = Array('chat/' . $channel['channel_address'] . (($has_chats) ? '' : '/new'), t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); + if(! UNO) + $nav['usermenu'][] = Array('chat/' . $channel['channel_address'] . (($has_chats) ? '' : '/new'), t('Chat'),"",t('Your chatrooms'),'chat_nav_btn'); require_once('include/menu.php'); $has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK); - if($has_bookmarks) { + if(($has_bookmarks) && (! UNO)) { $nav['usermenu'][] = Array('bookmarks', t('Bookmarks'), "", t('Your bookmarks'),'bookmarks_nav_btn'); } - if(feature_enabled($channel['channel_id'],'webpages')) + if(feature_enabled($channel['channel_id'],'webpages') && (! UNO)) $nav['usermenu'][] = Array('webpages/' . $channel['channel_address'],t('Webpages'),"",t('Your webpages'),'webpages_nav_btn'); } else { @@ -154,7 +155,8 @@ EOT; $nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'),'help_nav_btn'); - $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn'); + if(! UNO) + $nav['apps'] = array('apps', t('Apps'), "", t('Applications, utilities, links, games'),'apps_nav_btn'); $nav['search'] = array('search', t('Search'), "", t('Search site @name, #tag, ?docs, content')); @@ -196,8 +198,9 @@ EOT; $nav['all_events'] = array('events', t('Events'), "", t('Event Calendar'),'events_nav_btn'); $nav['all_events']['all']=array('events', t('See all events'), "", ""); $nav['all_events']['mark'] = array('', t('Mark all events seen'), '',''); - - $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); + + if(! UNO) + $nav['manage'] = array('manage', t('Channel Manager'), "", t('Manage Your Channels'),'manage_nav_btn'); $nav['settings'] = array('settings', t('Settings'),"", t('Account/Channel Settings'),'settings_nav_btn'); diff --git a/include/network.php b/include/network.php index 859a60650..93fd836f8 100644 --- a/include/network.php +++ b/include/network.php @@ -40,7 +40,7 @@ function z_fetch_url($url, $binary = false, $redirects = 0, $opts = array()) { $ch = @curl_init($url); if(($redirects > 8) || (! $ch)) - return false; + return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @curl_setopt($ch, CURLINFO_HEADER_OUT, true); @@ -171,7 +171,7 @@ function z_post_url($url,$params, $redirects = 0, $opts = array()) { $ch = curl_init($url); if(($redirects > 8) || (! $ch)) - return ret; + return $ret; @curl_setopt($ch, CURLOPT_HEADER, true); @curl_setopt($ch, CURLINFO_HEADER_OUT, true); @@ -294,8 +294,8 @@ function z_post_url_json($url, $params, $redirects = 0, $opts = array()) { } -function json_return_and_die($x) { - header("content-type: application/json"); +function json_return_and_die($x, $content_type = 'application/json') { + header("Content-type: $content_type"); echo json_encode($x); killme(); } @@ -1144,6 +1144,10 @@ function discover_by_webbie($webbie) { dbesc($addr) ); + // fix relative urls + if($vcard['photo'] && (strpos($vcard['photo'],'http') !== 0)) + $vcard['photo'] = $diaspora_base . '/' . $vcard['photo']; + /** * * Diaspora communications are notoriously unreliable and receiving profile update messages (indeed any messages) @@ -1615,18 +1619,19 @@ function format_and_send_email($sender,$xchan,$item) { // load the template for private message notifications $tpl = get_markup_template('email_notify_html.tpl'); $email_html_body = replace_macros($tpl,array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, + '$banner' => $banner, + '$notify_icon' => Zotlabs\Project\System::get_notify_icon(), + '$product' => $product, + '$preamble' => '', + '$sitename' => $sitename, + '$siteurl' => $siteurl, '$source_name' => $sender['xchan_name'], '$source_link' => $sender['xchan_url'], '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], + '$username' => $xchan['xchan_name'], '$hsitelink' => $datarray['hsitelink'], '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, + '$thanks' => $thanks, '$site_admin' => $site_admin, '$title' => $title, '$htmlversion' => $htmlversion, @@ -1635,20 +1640,20 @@ function format_and_send_email($sender,$xchan,$item) { // load the template for private message notifications $tpl = get_markup_template('email_notify_text.tpl'); $email_text_body = replace_macros($tpl, array( - '$banner' => $banner, - '$product' => $product, - '$preamble' => '', - '$sitename' => $sitename, - '$siteurl' => $siteurl, + '$banner' => $banner, + '$product' => $product, + '$preamble' => '', + '$sitename' => $sitename, + '$siteurl' => $siteurl, '$source_name' => $sender['xchan_name'], '$source_link' => $sender['xchan_url'], '$source_photo' => $sender['xchan_photo_m'], - '$username' => $xchan['xchan_name'], - '$hsitelink' => $datarray['hsitelink'], - '$hitemlink' => $datarray['hitemlink'], - '$thanks' => $thanks, + '$username' => $xchan['xchan_name'], + '$hsitelink' => $datarray['hsitelink'], + '$hitemlink' => $datarray['hitemlink'], + '$thanks' => $thanks, '$site_admin' => $site_admin, - '$title' => $title, + '$title' => $title, '$textversion' => $textversion )); @@ -1662,13 +1667,13 @@ function format_and_send_email($sender,$xchan,$item) { // use the EmailNotification library to send the message enotify::send(array( - 'fromName' => $product, - 'fromEmail' => $sender_email, - 'replyTo' => $sender_email, - 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), - 'messageSubject' => (($title) ? $title : t('No Subject')), - 'htmlVersion' => $email_html_body, - 'textVersion' => $email_text_body, + 'fromName' => $product, + 'fromEmail' => $sender_email, + 'replyTo' => $sender_email, + 'toEmail' => str_replace('mailto:','',$xchan['xchan_addr']), + 'messageSubject' => (($title) ? $title : t('No Subject')), + 'htmlVersion' => $email_html_body, + 'textVersion' => $email_text_body, 'additionalMailHeader' => '', )); @@ -1717,7 +1722,7 @@ function get_site_info() { global $a; $register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN'); - $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_SECONDARY','DIRECTORY_MODE_PRIMARY', 256 => 'DIRECTORY_MODE_STANDALONE'); + $directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_PRIMARY', 'DIRECTORY_MODE_SECONDARY', 256 => 'DIRECTORY_MODE_STANDALONE'); $sql_extra = ''; @@ -1763,16 +1768,13 @@ function get_site_info() { $site_info = get_config('system','info'); $site_name = get_config('system','sitename'); if(! get_config('system','hidden_version_siteinfo')) { - $version = RED_VERSION; - $tag = get_std_version(); + $version = Zotlabs\Project\System::get_project_version(); + $tag = Zotlabs\Project\System::get_std_version(); if(@is_dir('.git') && function_exists('shell_exec')) { $commit = trim( @shell_exec('git log -1 --format="%h"')); -// if(! get_config('system','hidden_tag_siteinfo')) -// $tag = trim( @shell_exec('git describe --tags --abbrev=0')); -// else -// $tag = ''; } + if(! isset($commit) || strlen($commit) > 16) $commit = ''; } @@ -1788,10 +1790,22 @@ function get_site_info() { $hide_in_statistics = intval(get_config('system','hide_in_statistics')); $site_expire = intval(get_config('system', 'default_expire_days')); + load_config('feature_lock'); + $locked_features = array(); + if(is_array($a->config['feature_lock']) && count($a->config['feature_lock'])) { + foreach($a->config['feature_lock'] as $k => $v) { + if($k === 'config_loaded') + continue; + $locked_features[$k] = intval($v); + } + } + + $data = Array( 'version' => $version, 'version_tag' => $tag, + 'server_role' => Zotlabs\Project\System::get_server_role(), 'commit' => $commit, 'url' => z_root(), 'plugins' => $visible_plugins, @@ -1799,12 +1813,13 @@ function get_site_info() { 'invitation_only' => intval(get_config('system','invitation_only')), 'directory_mode' => $directory_mode[get_config('system','directory_mode')], 'language' => get_config('system','language'), - 'rss_connections' => get_config('system','feed_contacts'), + 'rss_connections' => intval(get_config('system','feed_contacts')), 'expiration' => $site_expire, 'default_service_restrictions' => $service_class, + 'locked_features' => $locked_features, 'admin' => $admin, 'site_name' => (($site_name) ? $site_name : ''), - 'platform' => PLATFORM_NAME, + 'platform' => Zotlabs\Project\System::get_platform_name(), 'dbdriver' => $db->getdriver(), 'lastpoll' => get_config('system','lastpoll'), 'info' => (($site_info) ? $site_info : ''), diff --git a/include/notifier.php b/include/notifier.php index 50981df9d..628847d54 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -68,7 +68,6 @@ require_once('include/html2plain.php'); require_once('include/cli_startup.php'); require_once('include/zot.php'); require_once('include/queue_fn.php'); -require_once('include/session.php'); require_once('include/datetime.php'); require_once('include/items.php'); require_once('include/bbcode.php'); @@ -188,6 +187,7 @@ function notifier_run($argv, $argc){ $recipients[] = $r[0]['abook_xchan']; $private = false; $packet_type = 'refresh'; + $packet_recips = array(array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig'],'hash' => $r[0]['xchan_hash'])); } } } @@ -297,7 +297,7 @@ function notifier_run($argv, $argc){ $channel = $s[0]; if($channel['channel_hash'] !== $target_item['author_xchan'] && $channel['channel_hash'] !== $target_item['owner_xchan']) { - logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}"); + logger("notifier: Sending channel {$channel['channel_hash']} is not owner {$target_item['owner_xchan']} or author {$target_item['author_xchan']}", LOGGER_NORMAL, LOG_WARNING); return; } @@ -316,7 +316,7 @@ function notifier_run($argv, $argc){ return; if(strpos($r[0]['postopts'],'nodeliver') !== false) { - logger('notifier: target item is undeliverable', LOGGER_DEBUG); + logger('notifier: target item is undeliverable', LOGGER_DEBUG, LOG_NOTICE); return; } @@ -352,8 +352,8 @@ function notifier_run($argv, $argc){ // $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); - logger('notifier: top_level_post: ' . (($top_level_post) ? 'true' : 'false'), LOGGER_DATA); + 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 @@ -395,7 +395,7 @@ function notifier_run($argv, $argc){ // TODO verify this is needed - copied logic from same place in old code if(intval($target_item['item_deleted']) && (! intval($target_item['item_wall']))) { - logger('notifier: ignoring delete notification for non-wall item'); + logger('notifier: ignoring delete notification for non-wall item', LOGGER_NORMAL, LOG_NOTICE); return; } } @@ -410,18 +410,19 @@ function notifier_run($argv, $argc){ $x = $encoded_item; $x['title'] = 'private'; $x['body'] = 'private'; - logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA); + logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); stringify_array_elms($recipients); if(! $recipients) return; -// logger('notifier: recipients: ' . print_r($recipients,true)); +// logger('notifier: recipients: ' . print_r($recipients,true), LOGGER_NORMAL, LOG_DEBUG); $env_recips = (($private) ? array() : null); $details = q("select xchan_hash, xchan_instance_url, xchan_network, xchan_addr, xchan_guid, xchan_guid_sig from xchan where xchan_hash in (" . implode(',',$recipients) . ")"); + $recip_list = array(); if($details) { @@ -436,15 +437,42 @@ function notifier_run($argv, $argc){ if(! $delivery_options) format_and_send_email($channel,$d,$target_item); } + } + } - - } + $narr = array( + 'channel' => $channel, + 'env_recips' => $env_recips, + 'packet_recips' => $packet_recips, + 'recipients' => $recipients, + 'item' => $item, + 'target_item' => $target_item, + 'top_level_post' => $top_level_post, + 'private' => $private, + 'followup' => $followup, + 'relay_to_owner' => $relay_to_owner, + 'uplink' => $uplink, + 'cmd' => $cmd, + 'mail' => $mail, + 'location' => $location, + 'request' => $request, + 'normal_mode' => $normal_mode, + 'packet_type' => $packet_type, + 'walltowall' => $walltowall, + 'queued' => array() + ); + + call_hooks('notifier_process', $narr); + if($narr['queued']) { + foreach($narr['queued'] as $pq) + $deliveries[] = $pq; } + if(($private) && (! $env_recips)) { // shouldn't happen - logger('notifier: private message with no envelope recipients.' . print_r($argv,true)); + logger('notifier: private message with no envelope recipients.' . print_r($argv,true), LOGGER_NORMAL, LOG_NOTICE); } logger('notifier: recipients (may be delivered to more if public): ' . print_r($recip_list,true), LOGGER_DEBUG); @@ -459,7 +487,7 @@ function notifier_run($argv, $argc){ if(! $r) { - logger('notifier: no hubs'); + logger('notifier: no hubs', LOGGER_NORMAL, LOG_NOTICE); return; } @@ -482,7 +510,7 @@ function notifier_run($argv, $argc){ foreach($hubs as $hub) { if(in_array($hub['hubloc_url'],$dead_hubs)) { - logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG); + logger('skipping dead hub: ' . $hub['hubloc_url'], LOGGER_DEBUG, LOG_INFO); continue; } @@ -502,8 +530,8 @@ function notifier_run($argv, $argc){ } } - logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG); - + logger('notifier: will notify/deliver to these hubs: ' . print_r($hublist,true), LOGGER_DEBUG, LOG_DEBUG); + foreach($dhubs as $hub) { @@ -512,6 +540,7 @@ function notifier_run($argv, $argc){ $narr = array( 'channel' => $channel, 'env_recips' => $env_recips, + 'packet_recips' => $packet_recips, 'recipients' => $recipients, 'item' => $item, 'target_item' => $target_item, @@ -547,7 +576,7 @@ function notifier_run($argv, $argc){ $packet = null; if($packet_type === 'refresh' || $packet_type === 'purge') { - $packet = zot_build_packet($channel,$packet_type); + $packet = zot_build_packet($channel,$packet_type,(($packet_recips) ? $packet_recips : null)); } elseif($packet_type === 'request') { $packet = zot_build_packet($channel,$packet_type,$env_recips,$hub['hubloc_sitekey'],$hash, @@ -576,7 +605,7 @@ function notifier_run($argv, $argc){ )); // only create delivery reports for normal undeleted items - if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted'])) { + if(is_array($target_item) && array_key_exists('postopts',$target_item) && (! $target_item['item_deleted']) && (! get_config('system','disable_dreport'))) { q("insert into dreport ( dreport_mid, dreport_site, dreport_recip, dreport_result, dreport_time, dreport_xchan, dreport_queue ) values ( '%s','%s','%s','%s','%s','%s','%s' ) ", dbesc($target_item['mid']), dbesc($hub['hubloc_host']), diff --git a/include/oembed.php b/include/oembed.php index e50d34c7d..fb7a30e65 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -3,6 +3,34 @@ function oembed_replacecb($matches){ $embedurl=$matches[1]; + + // site white/black list + + if(($x = get_config('system','embed_deny'))) { + $l = explode("\n",$x); + if($l) { + foreach($l as $ll) { + if(trim($ll) && strpos($embedurl,trim($ll)) !== false) + return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + } + } + } + if(($x = get_config('system','embed_allow'))) { + $found = false; + $l = explode("\n",$x); + if($l) { + foreach($l as $ll) { + if(trim($ll) && strpos($embedurl,trim($ll)) !== false) { + $found = true; + break; + } + } + } + if(! $found) { + return '<a href="' . $embedurl . '">' . $embedurl . '</a>'; + } + } + // implements a personal embed white/black list for logged in members if(local_channel()) { if(($x = get_pconfig(local_channel(),'system','embed_deny'))) { @@ -53,6 +81,10 @@ function oembed_fetch_url($embedurl){ $a = get_app(); + $embedurl = str_replace('&','&', $embedurl); + +// logger('fetch: ' . $embedurl); + $txt = Cache::get($a->videowidth . $embedurl); if(strstr($txt,'youtu') && strstr(z_root(),'https:')) { @@ -78,7 +110,6 @@ function oembed_fetch_url($embedurl){ else { // try oembed autodiscovery $redirects = 0; - $result = z_fetch_url($embedurl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); if($result['success']) $html_text = $result['body']; @@ -88,8 +119,8 @@ function oembed_fetch_url($embedurl){ 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; @@ -121,20 +152,29 @@ function oembed_fetch_url($embedurl){ if ($txt[0]!="{") $txt='{"type":"error"}'; //save in cache - Cache::set($a->videowidth . $embedurl,$txt); + + if(! get_config('system','oembed_cache_disable')) + Cache::set($a->videowidth . $embedurl,$txt); } $j = json_decode($txt); $j->embedurl = $embedurl; + +// logger('fetch return: ' . print_r($j,true)); + return $j; + + } function oembed_format_object($j){ $a = get_app(); $embedurl = $j->embedurl; +// logger('format: ' . print_r($j,true)); + $jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null)); $ret="<span class='oembed ".$j->type."'>"; @@ -173,6 +213,14 @@ function oembed_format_object($j){ $ret.="<br>"; }; break; case "link": { + if($j->thumbnail_url) { + if(is_matrix_url($embedurl)) { + $embedurl = zid($embedurl); + $j->thumbnail_url = zid($j->thumbnail_url); + } + $ret = '<a href="' . $embedurl . '" ><img src="' . $j->thumbnail_url . '" alt="thumbnail" /></a><br /><br />'; + } + //$ret = "<a href='".$embedurl."'>".$j->title."</a>"; }; break; case "rich": { @@ -184,23 +232,29 @@ function oembed_format_object($j){ // 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 .= '<span class="bookmark-identifier">#^</span>' . "<a href='$embedurl' rel='oembed'>$embedlink</a>"; - $ret .= "<br>"; + $ret .= '<br />' . "<a href='$embedurl' rel='oembed'>$embedlink</a>"; + $ret .= "<br />"; if (isset($j->author_name)) $ret.=" by ".$j->author_name; if (isset($j->provider_name)) $ret.=" on ".$j->provider_name; } else { // add <a> for html2bbcode conversion - $ret .= "<a href='$embedurl' rel='oembed'/>"; + $ret .= "<br /><a href='$embedurl' rel='oembed'>$embedurl</a>"; } $ret.="<br style='clear:left'></span>"; return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret)); } function oembed_iframe($src,$width,$height) { - if(! $width || strstr($width,'%')) + $scroll = ' scrolling="no" '; + if(! $width || strstr($width,'%')) { $width = '640'; - if(! $height || strstr($height,'%')) + $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; @@ -209,7 +263,7 @@ function oembed_iframe($src,$width,$height) { // Make sure any children are sandboxed within their own iframe. - return '<iframe height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' + return '<iframe ' . $scroll . 'height="' . $height . '" width="' . $width . '" src="' . $s . '" frameborder="no" >' . t('Embedded content') . '</iframe>'; } diff --git a/include/permissions.php b/include/permissions.php index a8e761f87..e4cd9ddda 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -626,7 +626,7 @@ function get_role_perms($role) { $ret['channel_w_mail'] = PERMS_SPECIFIC; $ret['channel_w_chat'] = PERMS_SPECIFIC; $ret['channel_a_delegate'] = PERMS_SPECIFIC; - $ret['channel_r_storage'] = PERMS_PUBLIC; + $ret['channel_r_storage'] = PERMS_SPECIFIC; $ret['channel_w_storage'] = PERMS_SPECIFIC; $ret['channel_r_pages'] = PERMS_PUBLIC; $ret['channel_w_pages'] = PERMS_SPECIFIC; @@ -641,10 +641,10 @@ function get_role_perms($role) { $ret['directory_publish'] = true; $ret['online'] = false; $ret['perms_follow'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT + |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; $ret['perms_accept'] = PERMS_R_STREAM|PERMS_R_PROFILE|PERMS_R_ABOOK - |PERMS_W_STREAM|PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT + |PERMS_W_WALL|PERMS_W_COMMENT|PERMS_W_MAIL|PERMS_W_CHAT |PERMS_R_STORAGE|PERMS_R_PAGES|PERMS_A_REPUBLISH|PERMS_W_LIKE|PERMS_W_TAGWALL; $ret['channel_r_stream'] = PERMS_PUBLIC; $ret['channel_r_profile'] = PERMS_PUBLIC; @@ -864,10 +864,10 @@ function get_role_perms($role) { */ function get_roles() { $roles = array( - t('Social Networking') => array('social' => t('Mostly Public'), 'social_restricted' => t('Restricted'), 'social_private' => t('Private')), - t('Community Forum') => array('forum' => t('Mostly Public'), 'forum_restricted' => t('Restricted'), 'forum_private' => t('Private')), - t('Feed Republish') => array('feed' => t('Mostly Public'), 'feed_restricted' => t('Restricted')), - t('Special Purpose') => array('soapbox' => t('Celebrity/Soapbox'), 'repository' => t('Group Repository')), + t('Social Networking') => array('social' => t('Social - Mostly Public'), 'social_restricted' => t('Social - Restricted'), 'social_private' => t('Social - Private')), + t('Community Forum') => array('forum' => t('Forum - Mostly Public'), 'forum_restricted' => t('Forum - Restricted'), 'forum_private' => t('Forum - Private')), + t('Feed Republish') => array('feed' => t('Feed - Mostly Public'), 'feed_restricted' => t('Feed - Restricted')), + t('Special Purpose') => array('soapbox' => t('Special - Celebrity/Soapbox'), 'repository' => t('Special - Group Repository')), t('Other') => array('custom' => t('Custom/Expert Mode')) ); diff --git a/include/photo/photo_driver.php b/include/photo/photo_driver.php index 0de3b9c97..dce92d8da 100644 --- a/include/photo/photo_driver.php +++ b/include/photo/photo_driver.php @@ -69,6 +69,8 @@ abstract class photo_driver { abstract function cropImage($max,$x,$y,$w,$h); + abstract function cropImageRect($maxx,$maxy,$x,$y,$w,$h); + abstract function imageString(); @@ -229,6 +231,7 @@ abstract class photo_driver { $this->doScaleImage($dest_width,$dest_height); } + public function scaleImageSquare($dim) { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_gd.php b/include/photo/photo_gd.php index 2ac7287e4..24bdc204f 100644 --- a/include/photo/photo_gd.php +++ b/include/photo/photo_gd.php @@ -108,6 +108,23 @@ class photo_gd extends photo_driver { $this->setDimensions(); } + 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(); + } + + + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/photo/photo_imagick.php b/include/photo/photo_imagick.php index 3f84fd06c..32bb61342 100644 --- a/include/photo/photo_imagick.php +++ b/include/photo/photo_imagick.php @@ -163,6 +163,24 @@ class photo_imagick extends photo_driver { $this->doScaleImage($max,$max); } + 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->doScaleImage($maxx,$maxy); + } + public function imageString() { if(!$this->is_valid()) return FALSE; diff --git a/include/photos.php b/include/photos.php index 93511d2c0..5dd5f3c62 100644 --- a/include/photos.php +++ b/include/photos.php @@ -48,7 +48,7 @@ function photo_upload($channel, $observer, $args) { // 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 AccessList($channel); + $acl = new Zotlabs\Access\AccessList($channel); if(array_key_exists('directory',$args) && $args['directory']) $acl->set($args['directory']); if(array_key_exists('allow_cid',$args)) @@ -296,11 +296,11 @@ function photo_upload($channel, $observer, $args) { $photo_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . t('a new photo') . '[/zrl]'; - $album_link = '[zrl=' . z_root() . '/photos/album/' . bin2hex($album) . ']' . $album . '[/zrl]'; + $album_link = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/album/' . bin2hex($album) . ']' . $album . '[/zrl]'; $activity_format = sprintf(t('%1$s posted %2$s to %3$s','photo_upload'), $author_link, $photo_link, $album_link); - $summary = $activity_format . "\n\n" . (($args['body']) ? $args['body'] . "\n\n" : ''); + $summary = (($args['body']) ? $args['body'] : '') . '[footer]' . $activity_format . '[/footer]'; $obj_body = '[zrl=' . z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $photo_hash . ']' . $tag . z_root() . "/photo/{$photo_hash}-{$scale}." . $ph->getExt() . '[/zmg]' @@ -391,8 +391,8 @@ function photo_upload($channel, $observer, $args) { $arr['deny_cid'] = $ac['deny_cid']; $arr['deny_gid'] = $ac['deny_gid']; $arr['verb'] = ACTIVITY_POST; - $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; - $arr['object'] = json_encode($object); + $arr['obj_type'] = ACTIVITY_OBJ_PHOTO; + $arr['object'] = json_encode($object); $arr['tgt_type'] = ACTIVITY_OBJ_ALBUM; $arr['target'] = json_encode($target); $arr['item_wall'] = 1; @@ -400,7 +400,7 @@ function photo_upload($channel, $observer, $args) { $arr['item_thread_top'] = 1; $arr['item_private'] = intval($acl->is_private()); $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . $arr['mid']; - $arr['body'] = $summary; + $arr['body'] = $summary; // this one is tricky because the item and the photo have the same permissions, those of the photo. diff --git a/include/plugin.php b/include/plugin.php index 4a35a0170..bd844442f 100755 --- a/include/plugin.php +++ b/include/plugin.php @@ -291,7 +291,7 @@ function call_hooks($name, &$data = null) { $func = $hook[1]; $func($a, $data); } else { - // remove orphan hooks + q("DELETE FROM hook WHERE hook = '%s' AND file = '%s' AND function = '%s'", dbesc($name), dbesc($hook[0]), @@ -313,7 +313,6 @@ function call_hooks($name, &$data = null) { * * Version: 1.2.3 * * Author: John <profile url> * * Author: Jane <email> - * * Compat: Red [(version)], Friendica [(version)] * * *\endcode * @param string $plugin the name of the plugin @@ -325,8 +324,9 @@ function get_plugin_info($plugin){ 'name' => $plugin, 'description' => '', 'author' => array(), + 'maintainer' => array(), 'version' => '', - 'compat' => '' + 'requires' => '' ); if (!is_file("addon/$plugin/$plugin.php")) @@ -342,17 +342,16 @@ function get_plugin_info($plugin){ if ($l != ""){ list($k, $v) = array_map("trim", explode(":", $l, 2)); $k = strtolower($k); - if ($k == 'author'){ + if ($k == 'author' || $k == 'maintainer'){ $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); if ($r) { - $info['author'][] = array('name' => $m[1], 'link' => $m[2]); + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); } else { - $info['author'][] = array('name' => $v); - } - } else { - if (array_key_exists($k, $info)){ - $info[$k] = $v; + $info[$k][] = array('name' => $v); } + } + else { + $info[$k] = $v; } } } @@ -361,6 +360,51 @@ function get_plugin_info($plugin){ return $info; } +function check_plugin_versions($info) { + + if(! is_array($info)) + return true; + + if(array_key_exists('minversion',$info)) { + 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)) { + 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)) { + 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(! in_array($test,get_app()->plugins)) + $found = false; + } + } + if(! $found) + return false; + } + + return true; +} + + + /** * @brief Parse theme comment in search of theme infos. @@ -626,17 +670,6 @@ function get_markup_template($s, $root = '') { return $template; } -// return the standardised version. Since we can't easily compare -// before the STD_VERSION definition was applied, we have to treat -// all prior release versions the same. You can dig through them -// with other means (such as RED_VERSION) if necessary. - -function get_std_version() { - if(defined('STD_VERSION')) - return STD_VERSION; - return '0.0.0'; -} - function folder_exists($folder) { diff --git a/include/poller.php b/include/poller.php index 031fb4533..808b54ee5 100644 --- a/include/poller.php +++ b/include/poller.php @@ -205,7 +205,7 @@ function poller_run($argv, $argc){ $keep_reports = intval(get_config('system','expire_delivery_reports')); if($keep_reports === 0) - $keep_reports = 30; + $keep_reports = 10; q("delete from dreport where dreport_time < %s - INTERVAL %s", db_utcnow(), diff --git a/include/profile_selectors.php b/include/profile_selectors.php index a80677cb3..9f993f803 100644 --- a/include/profile_selectors.php +++ b/include/profile_selectors.php @@ -7,7 +7,7 @@ function gender_selector($current="",$suffix="") { call_hooks('gender_selector', $select); - $o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -24,7 +24,7 @@ function gender_selector_min($current="",$suffix="") { call_hooks('gender_selector_min', $select); - $o .= "<select name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"gender$suffix\" id=\"gender-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -44,7 +44,7 @@ function sexpref_selector($current="",$suffix="") { call_hooks('sexpref_selector', $select); - $o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -62,7 +62,7 @@ function sexpref_selector_min($current="",$suffix="") { call_hooks('sexpref_selector_min', $select); - $o .= "<select name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"sexual$suffix\" id=\"sexual-select$suffix\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -81,7 +81,7 @@ function marital_selector($current="",$suffix="") { call_hooks('marital_selector', $select); - $o .= "<select name=\"marital\" id=\"marital-select\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"marital\" id=\"marital-select\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); @@ -98,7 +98,7 @@ function marital_selector_min($current="",$suffix="") { call_hooks('marital_selector_min', $select); - $o .= "<select name=\"marital\" id=\"marital-select\" size=\"1\" >"; + $o .= "<select class=\"form-control\" name=\"marital\" id=\"marital-select\" size=\"1\" >"; foreach($select as $selection) { if($selection !== 'NOTRANSLATION') { $selected = (($selection == $current) ? ' selected="selected" ' : ''); diff --git a/include/queue_fn.php b/include/queue_fn.php index 1e53d7488..676620e2f 100644 --- a/include/queue_fn.php +++ b/include/queue_fn.php @@ -88,6 +88,7 @@ function queue_deliver($outq, $immediate = false) { } } else { + // zot sites should all have a site record, unless they've been dead for as long as // your site has existed. Since we don't know for sure what these sites are, // call them unknown @@ -100,6 +101,11 @@ function queue_deliver($outq, $immediate = false) { } } + $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. if($outq['outq_driver'] === 'post') { @@ -118,11 +124,31 @@ function queue_deliver($outq, $immediate = false) { dbesc($outq['outq_hash']) ); remove_queue_item($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. + + if(! $immediate) { + $x = q("select outq_hash from outq where outq_posturl = '%s' and outq_delivered = 0", + dbesc($outq['outq_posturl']) + ); + + $piled_up = array(); + if($x) { + foreach($x as $xx) { + $piled_up[] = $xx['outq_hash']; + } + } + if($piled_up) { + do_delivery($piled_up); + } + } } else { logger('deliver: queue post returned ' . $result['return_code'] . ' from ' . $outq['outq_posturl'],LOGGER_DEBUG); - update_queue_item($argv[$x]); + update_queue_item($outq['outq_posturl']); } return; } diff --git a/include/ratenotif.php b/include/ratenotif.php index e94f30247..2c636c710 100644 --- a/include/ratenotif.php +++ b/include/ratenotif.php @@ -11,7 +11,6 @@ function ratenotif_run($argv, $argc){ $a = get_app(); - require_once("session.php"); require_once("datetime.php"); require_once('include/items.php'); require_once('include/Contact.php'); diff --git a/include/reddav.php b/include/reddav.php index c592597a9..a0bd1b1fc 100644 --- a/include/reddav.php +++ b/include/reddav.php @@ -19,13 +19,13 @@ */ use Sabre\DAV; -use RedMatrix\RedDAV; +use Zotlabs\Storage; require_once('vendor/autoload.php'); require_once('include/attach.php'); -require_once('include/RedDAV/RedFile.php'); -require_once('include/RedDAV/RedDirectory.php'); -require_once('include/RedDAV/RedBasicAuth.php'); +//require_once('Zotlabs/Storage/File.php'); +//require_once('Zotlabs/Storage/Directory.php'); +//require_once('Zotlabs/Storage/BasicAuth.php'); /** * @brief Returns an array with viewable channels. @@ -51,7 +51,7 @@ function RedChannelList(&$auth) { if (perm_is_allowed($rr['channel_id'], $auth->observer, 'view_storage')) { logger('found channel: /cloud/' . $rr['channel_address'], LOGGER_DATA); // @todo can't we drop '/cloud'? It gets stripped off anyway in RedDirectory - $ret[] = new RedDAV\RedDirectory('/cloud/' . $rr['channel_address'], $auth); + $ret[] = new Zotlabs\Storage\Directory('/cloud/' . $rr['channel_address'], $auth); } } } @@ -167,9 +167,9 @@ function RedCollectionData($file, &$auth) { foreach ($r as $rr) { //logger('filename: ' . $rr['filename'], LOGGER_DEBUG); if (intval($rr['is_dir'])) { - $ret[] = new RedDAV\RedDirectory($path . '/' . $rr['filename'], $auth); + $ret[] = new Zotlabs\Storage\Directory($path . '/' . $rr['filename'], $auth); } else { - $ret[] = new RedDAV\RedFile($path . '/' . $rr['filename'], $rr, $auth); + $ret[] = new Zotlabs\Storage\File($path . '/' . $rr['filename'], $rr, $auth); } } @@ -204,7 +204,7 @@ function RedFileData($file, &$auth, $test = false) { if ((! $file) || ($file === '/')) { - return new RedDAV\RedDirectory('/', $auth); + return new Zotlabs\Storage\Directory('/', $auth); } $file = trim($file, '/'); @@ -274,7 +274,7 @@ function RedFileData($file, &$auth, $test = false) { if ($test) return true; // final component was a directory. - return new RedDAV\RedDirectory($file, $auth); + return new Zotlabs\Storage\Directory($file, $auth); } if ($errors) { @@ -293,9 +293,9 @@ function RedFileData($file, &$auth, $test = false) { return true; if (intval($r[0]['is_dir'])) { - return new RedDAV\RedDirectory($path . '/' . $r[0]['filename'], $auth); + return new Zotlabs\Storage\Directory($path . '/' . $r[0]['filename'], $auth); } else { - return new RedDAV\RedFile($path . '/' . $r[0]['filename'], $r[0], $auth); + return new Zotlabs\Storage\File($path . '/' . $r[0]['filename'], $r[0], $auth); } } return false; diff --git a/include/security.php b/include/security.php index d4ebe0024..215cc92cb 100644 --- a/include/security.php +++ b/include/security.php @@ -18,20 +18,13 @@ function authenticate_success($user_record, $login_initial = false, $interactive $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + $lastlog_updated = false; + if(x($user_record, 'account_id')) { $a->account = $user_record; $_SESSION['account_id'] = $user_record['account_id']; $_SESSION['authenticated'] = 1; - if($login_initial || $update_lastlog) { - q("update account set account_lastlog = '%s' where account_id = %d", - dbesc(datetime_convert()), - intval($_SESSION['account_id']) - ); - $a->account['account_lastlog'] = datetime_convert(); - call_hooks('logged_in', $a->account); - - } $uid_to_load = (((x($_SESSION,'uid')) && (intval($_SESSION['uid']))) ? intval($_SESSION['uid']) @@ -42,9 +35,19 @@ function authenticate_success($user_record, $login_initial = false, $interactive change_channel($uid_to_load); } + if($login_initial || $update_lastlog) { + q("update account set account_lastlog = '%s' where account_id = %d", + dbesc(datetime_convert()), + intval($_SESSION['account_id']) + ); + $a->account['account_lastlog'] = datetime_convert(); + $lastlog_updated = true; + call_hooks('logged_in', $a->account); + } + } - if($login_initial) { + if(($login_initial) && (! $lastlog_updated)) { call_hooks('logged_in', $user_record); @@ -145,7 +148,7 @@ function change_channel($change_channel) { * @return string additional SQL where statement */ -function permissions_sql($owner_id, $remote_observer = null) { +function permissions_sql($owner_id, $remote_observer = null, $table = '') { $local_channel = local_channel(); @@ -155,10 +158,14 @@ function permissions_sql($owner_id, $remote_observer = null) { * default permissions - anonymous user */ - $sql = " AND allow_cid = '' - AND allow_gid = '' - AND deny_cid = '' - AND deny_gid = '' + if($table) + $table .= '.'; + + + $sql = " AND {$table}allow_cid = '' + AND {$table}allow_gid = '' + AND {$table}deny_cid = '' + AND {$table}deny_gid = '' "; /** @@ -178,7 +185,7 @@ function permissions_sql($owner_id, $remote_observer = null) { */ else { - $observer = (($remote_observer) ? $remote_observer : get_observer_hash()); + $observer = ((! is_null($remote_observer)) ? $remote_observer : get_observer_hash()); if($observer) { $groups = init_groups_visitor($observer); @@ -190,8 +197,8 @@ function permissions_sql($owner_id, $remote_observer = null) { } $regexop = db_getfunc('REGEXP'); $sql = sprintf( - " AND ( NOT (deny_cid like '%s' OR deny_gid $regexop '%s') - AND ( allow_cid like '%s' OR allow_gid $regexop '%s' OR ( allow_cid = '' AND allow_gid = '') ) + " AND ( NOT ({$table}deny_cid like '%s' OR {$table}deny_gid $regexop '%s') + AND ( {$table}allow_cid like '%s' OR {$table}allow_gid $regexop '%s' OR ( {$table}allow_cid = '' AND {$table}allow_gid = '') ) ) ", dbesc(protect_sprintf( '%<' . $observer . '>%')), diff --git a/include/session.php b/include/session.php index 92004bc18..43bba528b 100644 --- a/include/session.php +++ b/include/session.php @@ -1,4 +1,5 @@ <?php + /** * @file include/session.php * @@ -14,8 +15,8 @@ $session_expire = 180000; function new_cookie($time) { $old_sid = session_id(); -// ??? This shouldn't have any effect if called after session_start() -// We probably need to set the session expiration and change the PHPSESSID cookie. + // ??? This shouldn't have any effect if called after session_start() + // We probably need to set the session expiration and change the PHPSESSID cookie. session_set_cookie_params($time); session_regenerate_id(false); @@ -108,8 +109,9 @@ ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); /* - * PHP function which sets our user-level session storage functions. + * Set our session storage functions. */ + session_set_save_handler( 'ref_session_open', 'ref_session_close', @@ -117,4 +119,17 @@ session_set_save_handler( 'ref_session_write', 'ref_session_destroy', 'ref_session_gc' -);
\ No newline at end of file +); + + + // Force cookies to be secure (https only) if this site is SSL enabled. Must be done before session_start(). + + if(intval(get_app()->config['system']['ssl_cookie_protection'])) { + $arr = session_get_cookie_params(); + session_set_cookie_params( + ((isset($arr['lifetime'])) ? $arr['lifetime'] : 0), + ((isset($arr['path'])) ? $arr['path'] : '/'), + ((isset($arr['domain'])) ? $arr['domain'] : get_app()->get_hostname()), + ((isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') ? true : false), + ((isset($arr['httponly'])) ? $arr['httponly'] : true)); + }
\ No newline at end of file diff --git a/include/taxonomy.php b/include/taxonomy.php index 0bf89a7c1..5ef106938 100644 --- a/include/taxonomy.php +++ b/include/taxonomy.php @@ -398,16 +398,17 @@ function get_things($profile_hash,$uid) { $things[$k] = null; foreach($r as $rr) { - $l = q("select xchan_name, xchan_url from likes left join xchan on likee = xchan_hash where + $l = q("select xchan_name, xchan_photo_s, xchan_url from likes left join xchan on likee = xchan_hash where target_type = '%s' and target_id = '%s' and channel_id = %d", dbesc(ACTIVITY_OBJ_THING), dbesc($rr['obj_obj']), intval($uid) ); - for($x = 0; $x < count($l); $x ++) + 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']] = array(); diff --git a/include/text.php b/include/text.php index dd52b16b6..621f4cf93 100644 --- a/include/text.php +++ b/include/text.php @@ -536,9 +536,10 @@ function attribute_contains($attr, $s) { * * @param string $msg Message to log * @param int $level A log level. + * @param int $priority - compatible with syslog */ -function logger($msg, $level = 0) { +function logger($msg, $level = LOGGER_NORMAL, $priority = LOG_INFO) { // turn off logger in install mode global $a; global $db; @@ -559,8 +560,8 @@ function logger($msg, $level = 0) { $where = basename($stack[0]['file']) . ':' . $stack[0]['line'] . ':' . $stack[1]['function'] . ': '; } - $s = datetime_convert() . ':' . session_id() . ' ' . $where . $msg . PHP_EOL; - $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'logged' => false); + $s = datetime_convert() . ':' . log_priority_str($priority) . ':' . session_id() . ':' . $where . $msg . PHP_EOL; + $pluginfo = array('filename' => $logfile, 'loglevel' => $level, 'message' => $s,'priority' => $priority, 'logged' => false); call_hooks('logger',$pluginfo); @@ -568,6 +569,23 @@ function logger($msg, $level = 0) { @file_put_contents($pluginfo['filename'], $pluginfo['message'], 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' + ); + + if($parr[$priority]) + return $parr[$priority]; + return 'LOG_UNDEFINED'; +} + /** * @brief This is a special logging facility for developers. * @@ -794,7 +812,7 @@ function contact_block() { ); if(count($r)) { - $contacts = sprintf( tt('%d Connection','%d Connections', $total),$total); + $contacts = t('Connections'); $micropro = Array(); foreach($r as $rr) { $rr['archived'] = (intval($rr['abook_archived']) ? true : false); @@ -807,7 +825,7 @@ function contact_block() { $o = replace_macros($tpl, array( '$contacts' => $contacts, '$nickname' => $a->profile['channel_address'], - '$viewconnections' => t('View Connections'), + '$viewconnections' => (($total > $shown) ? sprintf(t('View all %s connections'),$total) : ''), '$micropro' => $micropro, )); @@ -950,16 +968,24 @@ function get_poke_verbs() { // index is present tense verb // value is array containing past tense verb, translation of present, translation of past - $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')), - ); + 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')), + ); + + call_hooks('poke_verbs', $arr); + } - call_hooks('poke_verbs', $arr); return $arr; } @@ -972,7 +998,7 @@ function get_mood_verbs() { 'tired' => t('tired'), 'perky' => t('perky'), 'angry' => t('angry'), - 'stupefied' => t('stupified'), + 'stupefied' => t('stupefied'), 'puzzled' => t('puzzled'), 'interested' => t('interested'), 'bitter' => t('bitter'), @@ -1418,7 +1444,7 @@ function prepare_body(&$item,$attach = false) { call_hooks('prepare_body_init', $item); - + $s = ''; $photo = ''; $is_photo = ((($item['verb'] === ACTIVITY_POST) && ($item['obj_type'] === ACTIVITY_OBJ_PHOTO)) ? true : false); @@ -1428,7 +1454,7 @@ function prepare_body(&$item,$attach = false) { // if original photo width is <= 640px prepend it to item body if($object['link'][0]['width'] && $object['link'][0]['width'] <= 640) { - $s = '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; + $s .= '<div class="inline-photo-item-wrapper"><a href="' . zid(rawurldecode($object['id'])) . '" target="_blank"><img class="inline-photo-item" style="max-width:' . $object['link'][0]['width'] . 'px; width:100%; height:auto;" src="' . zid(rawurldecode($object['link'][0]['href'])) . '"></a></div>' . $s; } // if original photo width is > 640px make it a cover photo @@ -1438,7 +1464,7 @@ function prepare_body(&$item,$attach = false) { } } - $s = prepare_text($item['body'],$item['mimetype'], false); + $s .= prepare_text($item['body'],$item['mimetype'], false); $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event($item['object']) : false); @@ -2698,5 +2724,67 @@ function item_url_replace($channel,&$item,$old,$new) { $item['sig'] = base64url_encode(rsa_sign($item['body'],$channel['channel_prvkey'])); $item['item_verified'] = 1; } + + // @fixme item['plink'] and item['llink'] + +} + + +/** + * @brief Used to wrap ACL elements in angle brackets for storage. + * + * @param[in,out] array &$item + */ +function sanitise_acl(&$item) { + if (strlen($item)) + $item = '<' . notags(trim($item)) . '>'; + else + unset($item); +} + +/** + * @brief Convert an ACL array to a storable string. + * + * @param array $p + * @return array + */ +function perms2str($p) { + $ret = ''; + + if (is_array($p)) + $tmp = $p; + else + $tmp = explode(',', $p); + + if (is_array($tmp)) { + array_walk($tmp, 'sanitise_acl'); + $ret = implode('', $tmp); + } + + return $ret; +} + + +/** + * @brief Turn user/group ACLs stored as angle bracketed text into arrays. + * + * turn string array of angle-bracketed elements into string array + * e.g. "<123xyz><246qyo><sxo33e>" => array(123xyz,246qyo,sxo33e); + * + * @param string $s + * @return array + */ +function expand_acl($s) { + $ret = array(); + + if(strlen($s)) { + $t = str_replace('<','',$s); + $a = explode('>',$t); + foreach($a as $aa) { + if($aa) + $ret[] = $aa; + } + } + return $ret; } diff --git a/include/widgets.php b/include/widgets.php index 4b14d6c94..0d31133e7 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -8,7 +8,7 @@ require_once('include/dir_fns.php'); require_once('include/contact_widgets.php'); require_once('include/attach.php'); - +require_once('include/Contact.php'); function widget_profile($args) { $a = get_app(); @@ -16,6 +16,16 @@ function widget_profile($args) { return profile_sidebar($a->profile, $block, true); } +function widget_zcard($args) { + $a = get_app(); + $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + $channel = channelx_by_n($a->profile_uid); + return get_zcard($channel,get_observer_hash(),array('width' => 875)); +} + + + + // FIXME The problem with the next widget is that we don't have a search function for webpages that we can send the links to. // Then we should also provide an option to search webpages and conversations. @@ -170,8 +180,8 @@ function widget_follow($args) { } return replace_macros(get_markup_template('follow.tpl'),array( '$connect' => t('Add New Connection'), - '$desc' => t('Enter the channel address'), - '$hint' => t('Example: bob@example.com, http://example.com/barbara'), + '$desc' => t('Enter channel address'), + '$hint' => t('Examples: bob@example.com, https://example.com/barbara'), '$follow' => t('Connect'), '$abook_usage_message' => $abook_usage_message )); @@ -369,6 +379,17 @@ function widget_fullprofile($arr) { return profile_sidebar($a->profile, $block); } +function widget_shortprofile($arr) { + $a = get_app(); + if(! $a->profile['profile_uid']) + return; + + $block = (((get_config('system', 'block_public')) && (! local_channel()) && (! remote_channel())) ? true : false); + + return profile_sidebar($a->profile, $block, true, true); +} + + function widget_categories($arr) { $a = get_app(); @@ -493,37 +514,42 @@ function widget_settings_menu($arr) { 'selected' => ((argv(1) === 'channel') ? 'active' : ''), ), - array( - 'label' => t('Additional features'), - 'url' => $a->get_baseurl(true).'/settings/features', - 'selected' => ((argv(1) === 'features') ? 'active' : ''), - ), + ); - array( - 'label' => t('Feature/Addon settings'), - 'url' => $a->get_baseurl(true).'/settings/featured', - 'selected' => ((argv(1) === 'featured') ? 'active' : ''), - ), + if(get_features()) { + $tabs[] = array( + 'label' => t('Additional features'), + 'url' => $a->get_baseurl(true).'/settings/features', + 'selected' => ((argv(1) === 'features') ? 'active' : ''), + ); + } - array( - 'label' => t('Display settings'), - 'url' => $a->get_baseurl(true).'/settings/display', - 'selected' => ((argv(1) === 'display') ? 'active' : ''), - ), + $tabs[] = array( + 'label' => t('Feature/Addon settings'), + 'url' => $a->get_baseurl(true).'/settings/featured', + 'selected' => ((argv(1) === 'featured') ? 'active' : ''), + ); - array( - 'label' => t('Connected apps'), - 'url' => $a->get_baseurl(true) . '/settings/oauth', - 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), - ), + $tabs[] = array( + 'label' => t('Display settings'), + 'url' => $a->get_baseurl(true).'/settings/display', + 'selected' => ((argv(1) === 'display') ? 'active' : ''), + ); - array( + $tabs[] = array( + 'label' => t('Connected apps'), + 'url' => $a->get_baseurl(true) . '/settings/oauth', + 'selected' => ((argv(1) === 'oauth') ? 'active' : ''), + ); + + // IF can go away when UNO export and import is fully functional + if(! UNO) { + $tabs[] = array( 'label' => t('Export channel'), 'url' => $a->get_baseurl(true) . '/uexport', 'selected' => '' - ), - - ); + ); + } if($role === false || $role === 'custom') { $tabs[] = array( @@ -804,21 +830,39 @@ function widget_suggestedchats($arr) { } function widget_item($arr) { - // FIXME there is no $a here - $uid = $a->profile['profile_uid']; - if((! $uid) || (! $arr['mid'])) + + $channel_id = 0; + if(array_key_exists('channel_id',$arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = get_app()->profile_uid; + if(! $channel_id) return ''; - if(! perm_is_allowed($uid, get_observer_hash(), 'view_pages')) + + if((! $arr['mid']) && (! $arr['title'])) return ''; - require_once('include/security.php'); - $sql_extra = item_permissions_sql($uid); + if(! perm_is_allowed($channel_id, get_observer_hash(), 'view_pages')) + return ''; - $r = q("select * from item where mid = '%s' and uid = %d and item_type = " . intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1", - dbesc($arr['mid']), - intval($uid) - ); + require_once('include/security.php'); + $sql_extra = item_permissions_sql($channel_id); + + if($arr['title']) { + $r = q("select item.* from item left join item_id on item.id = item_id.iid + where item.uid = %d and sid = '%s' and service = 'WEBPAGE' and item_type = %d $sql_options $revision limit 1", + intval($channel_id), + dbesc($arr['title']), + intval(ITEM_TYPE_WEBPAGE) + ); + } + else { + $r = q("select * from item where mid = '%s' and uid = %d and item_type = " . intval(ITEM_TYPE_WEBPAGE) . " $sql_extra limit 1", + dbesc($arr['mid']), + intval($channel_id) + ); + } if(! $r) return ''; @@ -912,8 +956,8 @@ function widget_photo($arr) { // ensure they can't sneak in an eval(js) function - if(strpos($style,'(') !== false) - return ''; + if(strpbrk($style,'(\'"<>') !== false) + $style = ''; if(array_key_exists('zrl', $arr) && isset($arr['zrl'])) $zrl = (($arr['zrl']) ? true : false); @@ -933,6 +977,59 @@ function widget_photo($arr) { } +function widget_cover_photo($arr) { + + require_once('include/identity.php'); + $o = ''; + + $a = get_app(); + + $channel_id = 0; + if(array_key_exists('channel_id', $arr) && intval($arr['channel_id'])) + $channel_id = intval($arr['channel_id']); + if(! $channel_id) + $channel_id = $a->profile_uid; + if(! $channel_id) + return ''; + + $channel = channelx_by_n($channel_id); + + if(array_key_exists('style', $arr) && isset($arr['style'])) + $style = $arr['style']; + else + $style = 'width:100%; height: auto;'; + + // ensure they can't sneak in an eval(js) function + + if(strpbrk($style,'(\'"<>') !== false) + $style = ''; + + if(array_key_exists('title', $arr) && isset($arr['title'])) + $title = $arr['title']; + else + $title = $channel['channel_name']; + + if(array_key_exists('subtitle', $arr) && isset($arr['subtitle'])) + $subtitle = $arr['subtitle']; + else + $subtitle = $channel['xchan_addr']; + + $c = get_cover_photo($channel_id,'html'); + + if($c) { + $photo_html = (($style) ? str_replace('alt=',' style="' . $style . '" alt=',$c) : $c); + + $o = replace_macros(get_markup_template('cover_photo_widget.tpl'),array( + '$photo_html' => $photo_html, + '$title' => $title, + '$subtitle' => $subtitle, + '$hovertitle' => t('Click to show more'), + )); + } + return $o; +} + + function widget_photo_rand($arr) { require_once('include/photos.php'); @@ -1075,16 +1172,18 @@ function widget_rating($arr) { } + + $o = '<div class="widget">'; + $o .= '<h3>' . t('Rating Tools') . '</h3>'; + if((($remote) || (local_channel())) && (! $self)) { - $o = '<div class="widget rateme">'; if($remote) - $o .= '<a class="rateme" href="' . $url . '"><i class="icon-pencil"></i> ' . t('Rate Me') . '</a>'; + $o .= '<a class="btn btn-block btn-primary btn-sm" href="' . $url . '"><i class="icon-pencil"></i> ' . t('Rate Me') . '</a>'; else - $o .= '<div class="rateme fakelink" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="icon-pencil"></i> ' . t('Rate Me') . '</div>'; - $o .= '</div>'; + $o .= '<div class="btn btn-block btn-primary btn-sm" onclick="doRatings(\'' . $hash . '\'); return false;"><i class="icon-pencil"></i> ' . t('Rate Me') . '</div>'; } - $o .= '<div class="widget rateme"><a class="rateme" href="ratings/' . $hash . '"><i class="icon-eye-open"></i> ' . t('View Ratings') . '</a>'; + $o .= '<a class="btn btn-block btn-default btn-sm" href="ratings/' . $hash . '"><i class="icon-eye-open"></i> ' . t('View Ratings') . '</a>'; $o .= '</div>'; return $o; @@ -1119,7 +1218,7 @@ function widget_forums($arr) { $perms_sql = item_permissions_sql(local_channel()) . item_normal(); - $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and abook_channel = %d order by xchan_name $limit ", + $r1 = q("select * from abook left join xchan on abook_xchan = xchan_hash where ( xchan_pubforum = 1 or ((abook_their_perms & %d ) != 0 and (abook_their_perms & %d ) = 0) ) and xchan_deleted = 0 and abook_channel = %d order by xchan_name $limit ", intval(PERMS_W_TAGWALL), intval(PERMS_W_STREAM), intval(local_channel()) @@ -1214,10 +1313,12 @@ function widget_admin($arr) { 'site' => array(z_root() . '/admin/site/', t('Site'), 'site'), 'users' => array(z_root() . '/admin/users/', t('Accounts'), 'users'), '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'), 'plugins' => array(z_root() . '/admin/plugins/', t('Plugins'), 'plugins'), '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 Config'), 'profs'), + 'profs' => array(z_root() . '/admin/profs', t('Profile Fields'), 'profs'), 'dbsync' => array(z_root() . '/admin/dbsync/', t('DB updates'), 'dbsync') ); diff --git a/include/zot.php b/include/zot.php index 922637bc1..a60a50d8f 100644 --- a/include/zot.php +++ b/include/zot.php @@ -11,7 +11,6 @@ require_once('include/crypto.php'); require_once('include/items.php'); require_once('include/hubloc.php'); -require_once('include/DReport.php'); require_once('include/queue_fn.php'); @@ -142,7 +141,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot $data[$k] = $v; } - logger('zot_build_packet: ' . print_r($data,true), LOGGER_DATA); + logger('zot_build_packet: ' . print_r($data,true), LOGGER_DATA, LOG_DEBUG); // Hush-hush ultra top-secret mode @@ -194,7 +193,7 @@ function zot_finger($webbie, $channel = null, $autofallback = true) { logger('zot_finger: no address :' . $webbie); return array('success' => false); } - logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA); + logger('using xchan_addr: ' . $xchan_addr, LOGGER_DATA, LOG_DEBUG); // potential issue here; the xchan_addr points to the primary hub. // The webbie we were called with may not, so it might not be found @@ -211,7 +210,7 @@ function zot_finger($webbie, $channel = null, $autofallback = true) { if ($r[0]['hubloc_network'] && $r[0]['hubloc_network'] !== 'zot') { logger('zot_finger: alternate network: ' . $webbie); - logger('url: '.$url.', net: '.var_export($r[0]['hubloc_network'],true), LOGGER_DATA); + logger('url: '.$url.', net: '.var_export($r[0]['hubloc_network'],true), LOGGER_DATA, LOG_DEBUG); return array('success' => false); } } else { @@ -288,9 +287,9 @@ function zot_refresh($them, $channel = null, $force = false) { return true; } - logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA); + logger('zot_refresh: them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); if ($channel) - logger('zot_refresh: channel: ' . print_r($channel,true), LOGGER_DATA); + logger('zot_refresh: channel: ' . print_r($channel,true), LOGGER_DATA, LOG_DEBUG); $url = null; @@ -353,7 +352,7 @@ function zot_refresh($them, $channel = null, $force = false) { $result = z_post_url($url . $rhs,$postvars); - logger('zot_refresh: zot-info: ' . print_r($result,true), LOGGER_DATA); + logger('zot_refresh: zot-info: ' . print_r($result,true), LOGGER_DATA, LOG_DEBUG); if ($result['success']) { @@ -381,7 +380,7 @@ function zot_refresh($them, $channel = null, $force = false) { $channel['channel_prvkey']); if($permissions) $permissions = json_decode($permissions,true); - logger('decrypted permissions: ' . print_r($permissions,true), LOGGER_DATA); + logger('decrypted permissions: ' . print_r($permissions,true), LOGGER_DATA, LOG_DEBUG); } else $permissions = $j['permissions']; @@ -523,6 +522,11 @@ function zot_refresh($them, $channel = null, $force = false) { unset($new_connection[0]['abook_id']); unset($new_connection[0]['abook_account']); unset($new_connection[0]['abook_channel']); + + $abconfig = load_abconfig($channel['channel_hash'],$new_connection['abook_xchan']); + if($abconfig) + $new_connection['abconfig'] = $abconfig; + build_sync_packet($channel['channel_id'], array('abook' => $new_connection)); } } @@ -614,7 +618,7 @@ function zot_register_hub($arr) { $x = z_fetch_url($url); - logger('zot_register_hub: ' . print_r($x,true), LOGGER_DATA); + logger('zot_register_hub: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); if($x['success']) { $record = json_decode($x['body'],true); @@ -754,8 +758,8 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { dbesc($xchan_hash) ); - logger('import_xchan: update: existing: ' . print_r($r[0],true), LOGGER_DATA); - logger('import_xchan: update: new: ' . print_r($arr,true), LOGGER_DATA); + logger('import_xchan: update: existing: ' . print_r($r[0],true), LOGGER_DATA, LOG_DEBUG); + logger('import_xchan: update: new: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $what .= 'xchan '; $changed = true; } @@ -955,7 +959,7 @@ function import_xchan($arr,$ud_flags = UPDATE_FLAGS_UPDATED, $ud_arr = null) { $ret['hash'] = $xchan_hash; } - logger('import_xchan: result: ' . print_r($ret,true), LOGGER_DATA); + logger('import_xchan: result: ' . print_r($ret,true), LOGGER_DATA, LOG_DEBUG); return $ret; } @@ -980,7 +984,7 @@ function zot_process_response($hub, $arr, $outq) { if (! $x) { logger('zot_process_response: No json from ' . $hub); - logger('zot_process_response: headers: ' . print_r($arr['header'],true), LOGGER_DATA); + logger('zot_process_response: headers: ' . print_r($arr['header'],true), LOGGER_DATA, LOG_DEBUG); } if(is_array($x) && array_key_exists('delivery_report',$x) && is_array($x['delivery_report'])) { @@ -1015,7 +1019,7 @@ function zot_process_response($hub, $arr, $outq) { // async messages remain in the queue until processed. if(intval($outq['outq_async'])) - queue_set_delivered($outq['outq_hash'],$outq['outq_channel']); + remove_queue_item($outq['outq_hash'],$outq['outq_channel']); logger('zot_process_response: ' . print_r($x,true), LOGGER_DEBUG); } @@ -1037,7 +1041,7 @@ function zot_process_response($hub, $arr, $outq) { */ function zot_fetch($arr) { - logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA); + logger('zot_fetch: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $url = $arr['sender']['url'] . $arr['callback']; @@ -1134,7 +1138,7 @@ function zot_import($arr, $sender_url) { $i['notify'] = json_decode(crypto_unencapsulate($i['notify'],get_config('system','prvkey')),true); } - logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA); + logger('zot_import: notify: ' . print_r($i['notify'],true), LOGGER_DATA, LOG_DEBUG); $hub = zot_gethub($i['notify']['sender']); if((! $hub) || ($hub['hubloc_url'] != $sender_url)) { @@ -1151,7 +1155,7 @@ function zot_import($arr, $sender_url) { if(array_key_exists('message',$i) && array_key_exists('type',$i['message']) && $i['message']['type'] === 'rating') { // rating messages are processed only by directory servers - logger('Rating received: ' . print_r($arr,true), LOGGER_DATA); + logger('Rating received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); $result = process_rating_delivery($i['notify']['sender'],$i['message']); continue; } @@ -1261,8 +1265,8 @@ function zot_import($arr, $sender_url) { continue; } - logger('Activity received: ' . print_r($arr,true), LOGGER_DATA); - logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Activity received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Activity recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $relay = ((array_key_exists('flags',$i['message']) && in_array('relay',$i['message']['flags'])) ? true : false); $result = process_delivery($i['notify']['sender'],$arr,$deliveries,$relay,false,$message_request); @@ -1270,16 +1274,16 @@ function zot_import($arr, $sender_url) { elseif($i['message']['type'] === 'mail') { $arr = get_mail_elements($i['message']); - logger('Mail received: ' . print_r($arr,true), LOGGER_DATA); - logger('Mail recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Mail received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Mail recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_mail_delivery($i['notify']['sender'],$arr,$deliveries); } elseif($i['message']['type'] === 'profile') { $arr = get_profile_elements($i['message']); - logger('Profile received: ' . print_r($arr,true), LOGGER_DATA); - logger('Profile recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Profile received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Profile recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_profile_delivery($i['notify']['sender'],$arr,$deliveries); } @@ -1288,16 +1292,16 @@ function zot_import($arr, $sender_url) { $arr = $i['message']; - logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA); - logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Channel sync received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Channel sync recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_channel_sync_delivery($i['notify']['sender'],$arr,$deliveries); } elseif($i['message']['type'] === 'location') { $arr = $i['message']; - logger('Location message received: ' . print_r($arr,true), LOGGER_DATA); - logger('Location message recipients: ' . print_r($deliveries,true), LOGGER_DATA); + logger('Location message received: ' . print_r($arr,true), LOGGER_DATA, LOG_DEBUG); + logger('Location message recipients: ' . print_r($deliveries,true), LOGGER_DATA, LOG_DEBUG); $result = process_location_delivery($i['notify']['sender'],$arr,$deliveries); } @@ -1483,7 +1487,7 @@ function public_recips($msg) { } } - logger('public_recips: ' . print_r($r,true), LOGGER_DATA); + logger('public_recips: ' . print_r($r,true), LOGGER_DATA, LOG_DEBUG); return $r; } @@ -1501,7 +1505,7 @@ function public_recips($msg) { */ function allowed_public_recips($msg) { - logger('allowed_public_recips: ' . print_r($msg,true),LOGGER_DATA); + logger('allowed_public_recips: ' . print_r($msg,true),LOGGER_DATA, LOG_DEBUG); if(array_key_exists('public_scope',$msg['message'])) $scope = $msg['message']['public_scope']; @@ -1589,7 +1593,7 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ foreach($deliveries as $d) { $local_public = $public; - $DR = new DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); + $DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) @@ -2068,7 +2072,7 @@ function process_mail_delivery($sender, $arr, $deliveries) { foreach($deliveries as $d) { - $DR = new DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); + $DR = new Zotlabs\Zot\DReport(z_root(),$sender['hash'],$d['hash'],$arr['mid']); $r = q("select * from channel where channel_hash = '%s' limit 1", dbesc($d['hash']) @@ -2868,12 +2872,15 @@ function import_site($arr, $pubkey) { */ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { + if(UNO) + return; + $a = get_app(); logger('build_sync_packet'); if($packet) - logger('packet: ' . print_r($packet, true),LOGGER_DATA); + logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG); if(! $uid) $uid = local_channel(); @@ -2967,7 +2974,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { $interval = ((get_config('system','delivery_interval') !== false) ? intval(get_config('system','delivery_interval')) : 2 ); - logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA); + logger('build_sync_packet: packet: ' . print_r($info,true), LOGGER_DATA, LOG_DEBUG); + + $total = count($synchubs); foreach($synchubs as $hub) { $hash = random_string(); @@ -2982,7 +2991,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { )); proc_run('php', 'include/deliver.php', $hash); - if($interval) + $total = $total - 1; + + if($interval && $total) @time_sleep_until(microtime(true) + (float) $interval); } } @@ -2997,6 +3008,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) { */ function process_channel_sync_delivery($sender, $arr, $deliveries) { + if(UNO) + return; + require_once('include/import.php'); /** @FIXME this will sync red structures (channel, pconfig and abook). Eventually we need to make this application agnostic. */ @@ -3115,6 +3129,11 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { foreach($arr['abook'] as $abook) { + $abconfig = null; + + if(array_key_exists('abconfig',$abook) && is_array($abook['abconfig']) && count($abook['abconfig'])) + $abconfig = $abook['abconfig']; + if(! array_key_exists('abook_blocked',$abook)) { // convert from redmatrix $abook['abook_blocked'] = (($abook['abook_flags'] & 0x0001) ? 1 : 0); @@ -3186,8 +3205,9 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { logger('process_channel_sync_delivery: total_feeds service class limit exceeded'); continue; } - q("insert into abook ( abook_xchan, abook_channel ) values ('%s', %d ) ", + q("insert into abook ( abook_xchan, abook_account, abook_channel ) values ('%s', %d, %d ) ", dbesc($clean['abook_xchan']), + intval($channel['channel_account_id']), intval($channel['channel_id']) ); $total_friends ++; @@ -3204,8 +3224,13 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { } } - - + if($abconfig) { + // @fixme does not handle sync of del_abconfig + foreach($abconfig as $abc) { + if($abc['chan'] === $channel['channel_hash']) + set_abconfig($abc['chan'],$abc['xchan'],$abc['cat'],$abc['k'],$abc['v']); + } + } } } @@ -3403,14 +3428,12 @@ function process_channel_sync_delivery($sender, $arr, $deliveries) { // we should probably do this for all items, but usually we only send one. - require_once('include/DReport.php'); - if(array_key_exists('item',$arr) && is_array($arr['item'][0])) { - $DR = new DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed'); + $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],$arr['item'][0]['message_id'],'channel sync processed'); $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); } else - $DR = new DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered'); + $DR = new Zotlabs\Zot\DReport(z_root(),$d['hash'],$d['hash'],'sync packet','channel sync delivered'); $result[] = $DR->get(); @@ -3828,7 +3851,7 @@ function zotinfo($arr) { $ret['site']['channels'] = channel_total(); - $ret['site']['version'] = PLATFORM_NAME . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; + $ret['site']['version'] = Zotlabs\Project\System::get_platform_name() . ' ' . RED_VERSION . '[' . DB_UPDATE_VERSION . ']'; $ret['site']['admin'] = get_config('system','admin_email'); @@ -3848,7 +3871,7 @@ function zotinfo($arr) { $ret['site']['sellpage'] = get_config('system','sellpage'); $ret['site']['location'] = get_config('system','site_location'); $ret['site']['realm'] = get_directory_realm(); - $ret['site']['project'] = PLATFORM_NAME; + $ret['site']['project'] = Zotlabs\Project\System::get_platform_name(); } @@ -3864,7 +3887,7 @@ function zotinfo($arr) { function check_zotinfo($channel,$locations,&$ret) { -// logger('locations: ' . print_r($locations,true),LOGGER_DATA); +// logger('locations: ' . print_r($locations,true),LOGGER_DATA, LOG_DEBUG); // This function will likely expand as we find more things to detect and fix. // 1. Because magic-auth is reliant on it, ensure that the system channel has a valid hubloc @@ -3924,6 +3947,9 @@ function check_zotinfo($channel,$locations,&$ret) { function delivery_report_is_storable($dr) { + if(get_config('system','disable_dreport')) + return false; + call_hooks('dreport_is_storable',$dr); // let plugins accept or reject - if neither, continue on @@ -4085,7 +4111,7 @@ function zot_reply_pickup($data) { $sitekey = $hubsite['hubloc_sitekey']; - logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA); + logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA, LOG_DEBUG); if(rsa_verify($data['callback'],base64url_decode($data['callback_sig']),$sitekey)) { $forgery = false; |