diff options
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r-- | Zotlabs/Lib/Apps.php | 20 | ||||
-rw-r--r-- | Zotlabs/Lib/DB_Upgrade.php | 119 | ||||
-rw-r--r-- | Zotlabs/Lib/Enotify.php | 9 | ||||
-rw-r--r-- | Zotlabs/Lib/MarkdownSoap.php | 103 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWiki.php | 6 | ||||
-rw-r--r-- | Zotlabs/Lib/NativeWikiPage.php | 99 | ||||
-rw-r--r-- | Zotlabs/Lib/ThreadItem.php | 23 |
7 files changed, 319 insertions, 60 deletions
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php index 1432cbdcf..edf050b95 100644 --- a/Zotlabs/Lib/Apps.php +++ b/Zotlabs/Lib/Apps.php @@ -219,7 +219,7 @@ class Apps { 'Suggest Channels' => t('Suggest Channels'), 'Login' => t('Login'), 'Channel Manager' => t('Channel Manager'), - 'Grid' => t('Grid'), + 'Grid' => t('Activity'), 'Settings' => t('Settings'), 'Files' => t('Files'), 'Webpages' => t('Webpages'), @@ -245,9 +245,19 @@ class Apps { 'Profile Photo' => t('Profile Photo') ); - if(array_key_exists($arr['name'],$apps)) { - $arr['name'] = $apps[$arr['name']]; + if(array_key_exists('name',$arr)) { + if(array_key_exists($arr['name'],$apps)) { + $arr['name'] = $apps[$arr['name']]; + } + } + else { + for($x = 0; $x < count($arr); $x++) { + if(array_key_exists($arr[$x]['name'],$apps)) { + $arr[$x]['name'] = $apps[$arr[$x]['name']]; + } + } } + } @@ -360,7 +370,9 @@ class Apps { '$deleted' => $papp['deleted'], '$feature' => (($papp['embed']) ? false : true), '$featured' => ((strpos($papp['categories'], 'nav_featured_app') === false) ? false : true), - '$navapps' => (($mode == 'nav') ? true : false) + '$navapps' => (($mode == 'nav') ? true : false), + '$add' => t('Add to app-tray'), + '$remove' => t('Remove from app-tray') )); } diff --git a/Zotlabs/Lib/DB_Upgrade.php b/Zotlabs/Lib/DB_Upgrade.php new file mode 100644 index 000000000..bb72e7a05 --- /dev/null +++ b/Zotlabs/Lib/DB_Upgrade.php @@ -0,0 +1,119 @@ +<?php + +namespace Zotlabs\Lib; + + +class DB_Upgrade { + + public $config_name = ''; + public $func_prefix = ''; + + function __construct($db_revision) { + + $update_file = 'install/' . PLATFORM_NAME . '/update.php'; + if(! file_exists($update_file)) { + $update_file = 'install/update.php'; + $this->config_name = 'db_version'; + $this->func_prefix = 'update_r'; + } + else { + $this->config_name = PLATFORM_NAME . '_db_version'; + $this->func_prefix = PLATFORM_NAME . '_update_'; + } + + $build = get_config('system', $this->config_name, 0); + if(! intval($build)) + $build = set_config('system', $this->config_name, $db_revision); + + if($build == $db_revision) { + // Nothing to be done. + return; + } + else { + $stored = intval($build); + if(! $stored) { + logger('Critical: check_config unable to determine database schema version'); + return; + } + + $current = intval($db_revision); + + if(($stored < $current) && file_exists($update_file)) { + + Config::Load('database'); + + // We're reporting a different version than what is currently installed. + // Run any existing update scripts to bring the database up to current. + + require_once($update_file); + + // make sure that boot.php and update.php are the same release, we might be + // updating from git right this very second and the correct version of the update.php + // file may not be here yet. This can happen on a very busy site. + + if($db_revision == UPDATE_VERSION) { + for($x = $stored; $x < $current; $x ++) { + $func = $this->func_prefix . $x; + if(function_exists($func)) { + // There could be a lot of processes running or about to run. + // We want exactly one process to run the update command. + // So store the fact that we're taking responsibility + // after first checking to see if somebody else already has. + + // If the update fails or times-out completely you may need to + // delete the config entry to try again. + + if(get_config('database', $func)) + break; + set_config('database',$func, '1'); + // call the specific update + + $retval = $func(); + if($retval) { + + // Prevent sending hundreds of thousands of emails by creating + // a lockfile. + + $lockfile = 'store/[data]/mailsent'; + + if ((file_exists($lockfile)) && (filemtime($lockfile) > (time() - 86400))) + return; + @unlink($lockfile); + //send the administrator an e-mail + file_put_contents($lockfile, $x); + + $r = q("select account_language from account where account_email = '%s' limit 1", + dbesc(\App::$config['system']['admin_email']) + ); + push_lang(($r) ? $r[0]['account_language'] : 'en'); + + z_mail( + [ + 'toEmail' => \App::$config['system']['admin_email'], + 'messageSubject' => sprintf( t('Update Error at %s'), z_root()), + 'textVersion' => replace_macros(get_intltext_template('update_fail_eml.tpl'), + [ + '$sitename' => \App::$config['system']['sitename'], + '$siteurl' => z_root(), + '$update' => $x, + '$error' => sprintf( t('Update %s failed. See error logs.'), $x) + ] + ) + ] + ); + + //try the logger + logger('CRITICAL: Update Failed: ' . $x); + pop_lang(); + } + else { + set_config('database',$func, 'success'); + } + } + } + set_config('system', $this->config_name, $db_revision); + } + } + } + } +}
\ No newline at end of file diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php index 257687567..a10675a87 100644 --- a/Zotlabs/Lib/Enotify.php +++ b/Zotlabs/Lib/Enotify.php @@ -67,7 +67,7 @@ class Enotify { $sender_name = $product; $hostname = \App::get_hostname(); if(strpos($hostname,':')) - $hostname = substr($hostname,0,strpos($hostname,':')); + $hostname = substr($hostname,0,strpos($hostname,':')); // Do not translate 'noreply' as it must be a legal 7-bit email address @@ -77,7 +77,7 @@ class Enotify { $sender_email = get_config('system','from_email'); if(! $sender_email) - $sender_email = 'Administrator' . '@' . \App::get_hostname(); + $sender_email = 'Administrator' . '@' . $hostname; $sender_name = get_config('system','from_email_name'); if(! $sender_name) @@ -495,13 +495,14 @@ class Enotify { } } - $r = q("insert into notify (hash,xname,url,photo,created,aid,uid,link,parent,seen,ntype,verb,otype) - values('%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')", + $r = q("insert into notify (hash,xname,url,photo,created,msg,aid,uid,link,parent,seen,ntype,verb,otype) + values('%s','%s','%s','%s','%s','%s',%d,%d,'%s','%s',%d,%d,'%s','%s')", dbesc($datarray['hash']), dbesc($datarray['xname']), dbesc($datarray['url']), dbesc($datarray['photo']), dbesc($datarray['created']), + dbesc(''), // will fill this in below after the record is created intval($datarray['aid']), intval($datarray['uid']), dbesc($datarray['link']), diff --git a/Zotlabs/Lib/MarkdownSoap.php b/Zotlabs/Lib/MarkdownSoap.php new file mode 100644 index 000000000..534ad819f --- /dev/null +++ b/Zotlabs/Lib/MarkdownSoap.php @@ -0,0 +1,103 @@ +<?php + +namespace Zotlabs\Lib; + +/** + * MarkdownSoap + * Purify Markdown for storage + * $x = new MarkdownSoap($string_to_be_cleansed); + * $text = $x->clean(); + * + * What this does: + * 1. extracts code blocks and privately escapes them from processing + * 2. Run html purifier on the content + * 3. put back the code blocks + * 4. run htmlspecialchars on the entire content for safe storage + * + * At render time: + * $markdown = \Zotlabs\Lib\MarkdownSoap::unescape($text); + * $html = \Michelf\MarkdownExtra::DefaultTransform($markdown); + */ + + + +class MarkdownSoap { + + private $token; + + private $str; + + function __construct($s) { + $this->str = $s; + $this->token = random_string(20); + } + + + function clean() { + + $x = $this->extract_code($this->str); + + $x = $this->purify($x); + + $x = $this->putback_code($x); + + $x = $this->escape($x); + + return $x; + } + + function extract_code($s) { + + $text = preg_replace_callback('{ + (?:\n\n|\A\n?) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?> + [ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + }xm', + [ $this , 'encode_code' ], $s); + + return $text; + } + + function encode_code($matches) { + return $this->token . ';' . base64_encode($matches[0]) . ';' ; + } + + function decode_code($matches) { + return base64_decode($matches[1]); + } + + function putback_code($s) { + $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm',[ $this, 'decode_code' ], $s); + return $text; + } + + function purify($s) { + $s = $this->protect_autolinks($s); + $s = purify_html($s); + $s = $this->unprotect_autolinks($s); + return $s; + } + + function protect_autolinks($s) { + $s = preg_replace('/\<(https?\:\/\/)(.*?)\>/','[$1$2]($1$2)',$s); + return $s; + } + + function unprotect_autolinks($s) { + return $s; + + } + + function escape($s) { + return htmlspecialchars($s,ENT_QUOTES); + } + + static public function unescape($s) { + return htmlspecialchars_decode($s,ENT_QUOTES); + } +} diff --git a/Zotlabs/Lib/NativeWiki.php b/Zotlabs/Lib/NativeWiki.php index 519102d24..4301feaa0 100644 --- a/Zotlabs/Lib/NativeWiki.php +++ b/Zotlabs/Lib/NativeWiki.php @@ -75,6 +75,8 @@ class NativeWiki { $arr['obj_type'] = ACTIVITY_OBJ_WIKI; $arr['body'] = '[table][tr][td][h1]New Wiki[/h1][/td][/tr][tr][td][zrl=' . $wiki_url . ']' . $wiki['htmlName'] . '[/zrl][/td][/tr][/table]'; + $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel['channel_id'],'view_wiki'),true); + // Save the wiki name information using iconfig. This is shareable. if(! set_iconfig($arr, 'wiki', 'rawName', $wiki['rawName'], true)) { return array('item' => null, 'success' => false); @@ -99,11 +101,11 @@ class NativeWiki { static public function sync_a_wiki_item($uid,$id,$resource_id) { - $r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = %d )) ", + $r = q("SELECT * from item WHERE uid = %d AND ( id = %d OR ( resource_type = '%s' and resource_id = '%s' )) ", intval($uid), intval($id), dbesc(NWIKI_ITEM_RESOURCE_TYPE), - intval($resource_id) + dbesc($resource_id) ); if($r) { xchan_query($r); diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php index 1467a1cfb..ed3df436c 100644 --- a/Zotlabs/Lib/NativeWikiPage.php +++ b/Zotlabs/Lib/NativeWikiPage.php @@ -21,12 +21,23 @@ class NativeWikiPage { $sql_extra = item_permissions_sql($channel_id,$observer_hash); $r = q("select * from item where resource_type = 'nwikipage' and resource_id = '%s' and uid = %d and item_deleted = 0 - $sql_extra group by mid", + $sql_extra order by created asc", dbesc($resource_id), intval($channel_id) ); if($r) { - $items = fetch_post_tags($r,true); + $x = []; + $y = []; + + foreach($r as $rv) { + if(! in_array($rv['mid'],$x)) { + $y[] = $rv; + $x[] = $rv['mid']; + } + } + + $items = fetch_post_tags($y,true); + foreach($items as $page_item) { $title = get_iconfig($page_item['id'],'nwikipage','pagetitle',t('(No Title)')); if(urldecode($title) !== 'Home') { @@ -48,13 +59,23 @@ class NativeWikiPage { $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); + if (! $w['wiki']) { + return array('content' => null, 'message' => 'Error reading wiki', 'success' => false); + } + // create an empty activity $arr = []; - $arr['uid'] = $channel_id; - $arr['author_xchan'] = $observer_hash; + $arr['uid'] = $channel_id; + $arr['author_xchan'] = $observer_hash; $arr['resource_type'] = 'nwikipage'; - $arr['resource_id'] = $resource_id; + $arr['resource_id'] = $resource_id; + $arr['allow_cid'] = $w['wiki']['allow_cid']; + $arr['allow_gid'] = $w['wiki']['allow_gid']; + $arr['deny_cid'] = $w['wiki']['deny_cid']; + $arr['deny_gid'] = $w['wiki']['deny_gid']; + + $arr['public_policy'] = map_scope(\Zotlabs\Access\PermissionLimits::Get($channel_id,'view_wiki'),true); // We may wish to change this some day. $arr['item_unpublished'] = 1; @@ -146,7 +167,7 @@ class NativeWikiPage { $content = $item['body']; return [ - 'content' => json_encode($content), + 'content' => $content, 'mimeType' => $w['mimeType'], 'message' => '', 'success' => true @@ -180,7 +201,7 @@ class NativeWikiPage { $processed ++; $history[] = [ 'revision' => $item['revision'], - 'date' => datetime_convert('UTC',date_default_timezone_get(),$item['created']), + 'date' => datetime_convert('UTC',date_default_timezone_get(),$item['edited']), 'name' => $item['author']['xchan_name'], 'title' => get_iconfig($item,'nwikipage','commit_msg') ]; @@ -225,6 +246,7 @@ class NativeWikiPage { } $sql_extra = item_permissions_sql($channel_id,$observer_hash); + if($revision == (-1)) $sql_extra .= " order by revision desc "; elseif($revision) @@ -277,6 +299,7 @@ class NativeWikiPage { } $sql_extra = item_permissions_sql($channel_id,$observer_hash); + $sql_extra .= " order by revision desc "; $r = null; @@ -295,48 +318,22 @@ class NativeWikiPage { return null; } - - - static public function prepare_content($s) { - - $text = preg_replace_callback('{ - (?:\n\n|\A\n?) - ( # $1 = the code block -- one or more lines, starting with a space/tab - (?> - [ ]{'.'4'.'} # Lines must start with a tab or a tab-width of spaces - .*\n+ - )+ - ) - ((?=^[ ]{0,'.'4'.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc - }xm', - 'self::nwiki_prepare_content_callback', $s); - - return $text; - } - - static public function nwiki_prepare_content_callback($matches) { - $codeblock = $matches[1]; - - $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES, UTF8, false); - return "\n\n" . $codeblock ; - } - - - static public function save_page($arr) { - $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); - $content = ((array_key_exists('content',$arr)) ? purify_html(Zlib\NativeWikiPage::prepare_content($arr['content'])) : ''); - $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); + $pageUrlName = ((array_key_exists('pageUrlName',$arr)) ? $arr['pageUrlName'] : ''); + $content = ((array_key_exists('content',$arr)) ? $arr['content'] : ''); + $resource_id = ((array_key_exists('resource_id',$arr)) ? $arr['resource_id'] : ''); $observer_hash = ((array_key_exists('observer_hash',$arr)) ? $arr['observer_hash'] : ''); $channel_id = ((array_key_exists('channel_id',$arr)) ? $arr['channel_id'] : 0); - $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0); + $revision = ((array_key_exists('revision',$arr)) ? $arr['revision'] : 0); $w = Zlib\NativeWiki::get_wiki($channel_id, $observer_hash, $resource_id); if (!$w['wiki']) { return array('message' => t('Error reading wiki'), 'success' => false); } + + $mimetype = $w['mimeType']; // fetch the most recently saved revision. @@ -355,6 +352,7 @@ class NativeWikiPage { $item['author_xchan'] = $observer_hash; $item['revision'] = (($arr['revision']) ? intval($arr['revision']) + 1 : intval($item['revision']) + 1); $item['edited'] = datetime_convert(); + $item['mimetype'] = $mimetype; if($item['iconfig'] && is_array($item['iconfig']) && count($item['iconfig'])) { for($x = 0; $x < count($item['iconfig']); $x ++) { @@ -522,6 +520,29 @@ class NativeWikiPage { } return $s; } + + static public function render_page_history($arr) { + + $pageUrlName = ((array_key_exists('pageUrlName', $arr)) ? $arr['pageUrlName'] : ''); + $resource_id = ((array_key_exists('resource_id', $arr)) ? $arr['resource_id'] : ''); + + $pageHistory = self::page_history([ + 'channel_id' => \App::$profile_uid, + 'observer_hash' => get_observer_hash(), + 'resource_id' => $resource_id, + 'pageUrlName' => $pageUrlName + ]); + + return replace_macros(get_markup_template('nwiki_page_history.tpl'), array( + '$pageHistory' => $pageHistory['history'], + '$permsWrite' => $arr['permsWrite'], + '$name_lbl' => t('Name'), + '$msg_label' => t('Message','wiki_history') + )); + + } + + /** * Replace the instances of the string [toc] with a list element that will be populated by diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php index 0ee8e6680..5910ea672 100644 --- a/Zotlabs/Lib/ThreadItem.php +++ b/Zotlabs/Lib/ThreadItem.php @@ -82,7 +82,8 @@ class ThreadItem { $dropping = false; $star = false; $isstarred = "unstarred fa-star-o"; - $indent = ''; + $is_comment = false; + $is_item = false; $osparkle = ''; $total_children = $this->count_descendants(); $unseen_comments = (($item['real_uid']) ? 0 : $this->count_unseen_descendants()); @@ -136,7 +137,7 @@ class ThreadItem { $filer = ((($conv->get_profile_owner() == local_channel()) && (! array_key_exists('real_uid',$item))) ? t("Save to Folder") : false); $profile_avatar = $item['author']['xchan_photo_m']; - $profile_link = chanlink_url($item['author']['xchan_url']); + $profile_link = chanlink_hash($item['author_xchan']); $profile_name = $item['author']['xchan_name']; $location = format_location($item); @@ -183,7 +184,7 @@ class ThreadItem { $like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : ''); if (count($like_list) > MAX_LIKERS) { $like_list_part = array_slice($like_list, 0, MAX_LIKERS); - array_push($like_list_part, '<a href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); + array_push($like_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#likeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); } else { $like_list_part = ''; } @@ -195,7 +196,7 @@ class ThreadItem { $dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun'); if (count($dislike_list) > MAX_LIKERS) { $dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS); - array_push($dislike_list_part, '<a href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); + array_push($dislike_list_part, '<a class="dropdown-item" href="#" data-toggle="modal" data-target="#dislikeModal-' . $this->get_id() . '"><b>' . t('View all') . '</b></a>'); } else { $dislike_list_part = ''; } @@ -232,7 +233,7 @@ class ThreadItem { } } else { - $indent = 'comment'; + $is_comment = true; } @@ -276,13 +277,13 @@ class ThreadItem { $keep_reports = intval(get_config('system','expire_delivery_reports')); if($keep_reports === 0) - $keep_reports = 30; + $keep_reports = 10; if((! get_config('system','disable_dreport')) && strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC',"now - $keep_reports days")) > 0) $dreport = t('Delivery Report'); if(strcmp(datetime_convert('UTC','UTC',$item['created']),datetime_convert('UTC','UTC','now - 12 hours')) > 0) - $indent .= ' shiny'; + $is_new = true; localize_item($item); @@ -337,7 +338,6 @@ class ThreadItem { 'profile_url' => $profile_link, 'thread_action_menu' => thread_action_menu($item,$conv->get_mode()), 'thread_author_menu' => thread_author_menu($item,$conv->get_mode()), - 'item_photo_menu' => item_photo_menu($item), 'dreport' => $dreport, 'name' => $profile_name, 'thumb' => $profile_avatar, @@ -361,7 +361,8 @@ class ThreadItem { 'attend_title' => t('Attendance Options'), 'vote_label' => t('Vote'), 'vote_title' => t('Voting Options'), - 'indent' => $indent, + 'is_comment' => $is_comment, + 'is_new' => $is_new, 'owner_url' => $this->get_owner_url(), 'owner_photo' => $this->get_owner_photo(), 'owner_name' => $this->get_owner_name(), @@ -407,7 +408,7 @@ class ThreadItem { 'showlike' => $showlike, 'showdislike' => $showdislike, 'comment' => $this->get_comment_box($indent), - 'previewing' => ($conv->is_preview() ? ' preview ' : ''), + 'previewing' => ($conv->is_preview() ? true : false ), 'wait' => t('Please wait'), 'submid' => str_replace(['+','='], ['',''], base64_encode(substr($item['mid'],0,32))), 'thread_level' => $thread_level @@ -767,7 +768,7 @@ class ThreadItem { return; if($this->is_toplevel() && ($this->get_data_value('author_xchan') != $this->get_data_value('owner_xchan'))) { - $this->owner_url = chanlink_url($this->data['owner']['xchan_url']); + $this->owner_url = chanlink_hash($this->data['owner']['xchan_hash']); $this->owner_photo = $this->data['owner']['xchan_photo_m']; $this->owner_name = $this->data['owner']['xchan_name']; $this->wall_to_wall = true; |