aboutsummaryrefslogtreecommitdiffstats
path: root/Zotlabs/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Zotlabs/Lib')
-rw-r--r--Zotlabs/Lib/Apps.php3
-rw-r--r--Zotlabs/Lib/Enotify.php2
-rw-r--r--Zotlabs/Lib/Img_filesize.php122
-rw-r--r--Zotlabs/Lib/MarkdownSoap.php65
-rw-r--r--Zotlabs/Lib/NativeWikiPage.php4
-rw-r--r--Zotlabs/Lib/Permcat.php78
-rw-r--r--Zotlabs/Lib/ThreadItem.php21
-rw-r--r--Zotlabs/Lib/ThreadStream.php17
8 files changed, 269 insertions, 43 deletions
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index 9271cee85..d2a307fd5 100644
--- a/Zotlabs/Lib/Apps.php
+++ b/Zotlabs/Lib/Apps.php
@@ -221,6 +221,7 @@ class Apps {
static public function translate_system_apps(&$arr) {
$apps = array(
'Apps' => t('Apps'),
+ 'Articles' => t('Articles'),
'Cards' => t('Cards'),
'Admin' => t('Site Admin'),
'Report Bug' => t('Report Bug'),
@@ -352,7 +353,7 @@ class Apps {
break;
default:
if($config)
- $unset = ((get_config('system', $require[0]) == $require[1]) ? false : true);
+ $unset = ((get_config('system', $require[0]) === $require[1]) ? false : true);
else
$unset = ((local_channel() && feature_enabled(local_channel(),$require)) ? false : true);
if($unset)
diff --git a/Zotlabs/Lib/Enotify.php b/Zotlabs/Lib/Enotify.php
index 697b57b6a..a7b4f28e8 100644
--- a/Zotlabs/Lib/Enotify.php
+++ b/Zotlabs/Lib/Enotify.php
@@ -804,6 +804,8 @@ class Enotify {
'when' => relative_date($item['created']),
'class' => (intval($item['item_unseen']) ? 'notify-unseen' : 'notify-seen'),
'b64mid' => ((in_array($item['verb'], [ACTIVITY_LIKE, ACTIVITY_DISLIKE])) ? 'b64.' . base64url_encode($item['thr_parent']) : 'b64.' . base64url_encode($item['mid'])),
+ 'notify_id' => 'undefined',
+ 'thread_top' => (($item['item_thread_top']) ? true : false),
'message' => strip_tags(bbcode($itemem_text))
);
diff --git a/Zotlabs/Lib/Img_filesize.php b/Zotlabs/Lib/Img_filesize.php
new file mode 100644
index 000000000..196697733
--- /dev/null
+++ b/Zotlabs/Lib/Img_filesize.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Zotlabs\Lib;
+
+class Img_filesize {
+
+ private $url;
+
+ function __construct($url) {
+ $this->url = $url;
+ }
+
+ function getSize() {
+ $size = null;
+
+ if(stripos($this->url,z_root() . '/photo') !== false) {
+ $size = self::getLocalFileSize($this->url);
+ }
+ if(! $size) {
+ $size = getRemoteFileSize($this->url);
+ }
+
+ return $size;
+ }
+
+
+ static function getLocalFileSize($url) {
+
+ $fname = basename($url);
+ $resolution = 0;
+
+ if(strpos($fname,'.') !== false)
+ $fname = substr($fname,0,strpos($fname,'.'));
+
+ if(substr($fname,-2,1) == '-') {
+ $resolution = intval(substr($fname,-1,1));
+ $fname = substr($fname,0,-2);
+ }
+
+ $r = q("SELECT filesize FROM photo WHERE resource_id = '%s' AND imgscale = %d LIMIT 1",
+ dbesc($fname),
+ intval($resolution)
+ );
+ if($r) {
+ return $r[0]['filesize'];
+ }
+ return null;
+ }
+
+}
+
+/**
+ * Try to determine the size of a remote file by making an HTTP request for
+ * a byte range, or look for the content-length header in the response.
+ * The function aborts the transfer as soon as the size is found, or if no
+ * length headers are returned, it aborts the transfer.
+ *
+ * @return int|null null if size could not be determined, or length of content
+ */
+function getRemoteFileSize($url)
+{
+ $ch = curl_init($url);
+
+ $headers = array(
+ 'Range: bytes=0-1',
+ 'Connection: close',
+ );
+
+ $in_headers = true;
+ $size = null;
+
+ curl_setopt($ch, CURLOPT_HEADER, 1);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2450.0 Iron/46.0.2450.0');
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+ curl_setopt($ch, CURLOPT_VERBOSE, 0); // set to 1 to debug
+ curl_setopt($ch, CURLOPT_STDERR, fopen('php://output', 'r'));
+
+ curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $line) use (&$in_headers, &$size) {
+ $length = strlen($line);
+
+ if (trim($line) == '') {
+ $in_headers = false;
+ }
+
+ list($header, $content) = explode(':', $line, 2);
+ $header = strtolower(trim($header));
+
+ if ($header == 'content-range') {
+ // found a content-range header
+ list($rng, $s) = explode('/', $content, 2);
+ $size = (int)$s;
+ return 0; // aborts transfer
+ } else if ($header == 'content-length' && 206 != curl_getinfo($curl, CURLINFO_HTTP_CODE)) {
+ // found content-length header and this is not a 206 Partial Content response (range response)
+ $size = (int)$content;
+ return 0;
+ } else {
+ // continue
+ return $length;
+ }
+ });
+
+ curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($in_headers) {
+ if (!$in_headers) {
+ // shouldn't be here unless we couldn't determine file size
+ // abort transfer
+ return 0;
+ }
+
+ // write function is also called when reading headers
+ return strlen($data);
+ });
+
+ curl_exec($ch);
+ curl_getinfo($ch);
+ curl_close($ch);
+
+ return $size;
+} \ No newline at end of file
diff --git a/Zotlabs/Lib/MarkdownSoap.php b/Zotlabs/Lib/MarkdownSoap.php
index fa279b07c..a58a5753a 100644
--- a/Zotlabs/Lib/MarkdownSoap.php
+++ b/Zotlabs/Lib/MarkdownSoap.php
@@ -3,51 +3,66 @@
namespace Zotlabs\Lib;
/**
- * MarkdownSoap
+ * @brief MarkdownSoap class.
+ *
* Purify Markdown for storage
+ * @code{.php}
* $x = new MarkdownSoap($string_to_be_cleansed);
* $text = $x->clean();
- *
+ * @endcode
* 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:
+ * At render time:
+ * @code{.php}
* $markdown = \Zotlabs\Lib\MarkdownSoap::unescape($text);
* $html = \Michelf\MarkdownExtra::DefaultTransform($markdown);
+ * @endcode
*/
-
-
-
class MarkdownSoap {
+ /**
+ * @var string
+ */
+ private $str;
+ /**
+ * @var string
+ */
private $token;
- private $str;
function __construct($s) {
- $this->str = $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->putback_code($x);
$x = $this->escape($x);
-
+
return $x;
}
+ /**
+ * @brief Extracts code blocks and privately escapes them from processing.
+ *
+ * @see encode_code()
+ * @see putback_code()
+ *
+ * @param string $s
+ * @return string
+ */
function extract_code($s) {
-
+
$text = preg_replace_callback('{
(?:\n\n|\A\n?)
( # $1 = the code block -- one or more lines, starting with a space/tab
@@ -62,7 +77,7 @@ class MarkdownSoap {
return $text;
}
-
+
function encode_code($matches) {
return $this->token . ';' . base64_encode($matches[0]) . ';' ;
}
@@ -71,8 +86,17 @@ class MarkdownSoap {
return base64_decode($matches[1]);
}
+ /**
+ * @brief Put back the code blocks.
+ *
+ * @see extract_code()
+ * @see decode_code()
+ *
+ * @param string $s
+ * @return string
+ */
function putback_code($s) {
- $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm',[ $this, 'decode_code' ], $s);
+ $text = preg_replace_callback('{' . $this->token . '\;(.*?)\;}xm', [ $this, 'decode_code' ], $s);
return $text;
}
@@ -84,20 +108,25 @@ class MarkdownSoap {
}
function protect_autolinks($s) {
- $s = preg_replace('/\<(https?\:\/\/)(.*?)\>/','[$1$2]($1$2)',$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,'UTF-8',false);
+ return htmlspecialchars($s, ENT_QUOTES, 'UTF-8', false);
}
+ /**
+ * @brief Converts special HTML entities back to characters.
+ *
+ * @param string $s
+ * @return string
+ */
static public function unescape($s) {
- return htmlspecialchars_decode($s,ENT_QUOTES);
+ return htmlspecialchars_decode($s, ENT_QUOTES);
}
}
diff --git a/Zotlabs/Lib/NativeWikiPage.php b/Zotlabs/Lib/NativeWikiPage.php
index 209a5ef3c..919c51276 100644
--- a/Zotlabs/Lib/NativeWikiPage.php
+++ b/Zotlabs/Lib/NativeWikiPage.php
@@ -68,6 +68,9 @@ class NativeWikiPage {
return array('content' => null, 'message' => 'Error reading wiki', 'success' => false);
}
+ // backslashes won't work well in the javascript functions
+ $name = str_replace('\\','',$name);
+
// create an empty activity
$arr = [];
@@ -351,6 +354,7 @@ class NativeWikiPage {
// fetch the most recently saved revision.
$item = self::load_page($arr);
+
if(! $item) {
return array('message' => t('Page not found'), 'success' => false);
}
diff --git a/Zotlabs/Lib/Permcat.php b/Zotlabs/Lib/Permcat.php
index 505ee2cfc..ca4aed9ed 100644
--- a/Zotlabs/Lib/Permcat.php
+++ b/Zotlabs/Lib/Permcat.php
@@ -2,12 +2,36 @@
namespace Zotlabs\Lib;
-use \Zotlabs\Access as Zaccess;
-
+use Zotlabs\Access\PermissionRoles;
+use Zotlabs\Access\Permissions;
+
+/**
+ * @brief Permission Categories. Permission rules for various classes of connections.
+ *
+ * Connection permissions answer the question "Can Joe view my photos?"
+ *
+ * Some permissions may be inherited from the channel's "privacy settings"
+ * (@ref ::Zotlabs::Access::PermissionLimits "PermissionLimits") "Who can view my
+ * photos (at all)?" which have higher priority than individual connection settings.
+ * We evaluate permission limits first, and then fall through to connection
+ * permissions if the permission limits didn't already make a definitive decision.
+ *
+ * After PermissionLimits and connection permissions are evaluated, individual
+ * content ACLs are evaluated (@ref ::Zotlabs::Access::AccessList "AccessList").
+ * These answer the question "Can Joe view *this* album/photo?".
+ */
class Permcat {
+ /**
+ * @var array
+ */
private $permcats = [];
+ /**
+ * @brief Permcat constructor.
+ *
+ * @param int $channel_id
+ */
public function __construct($channel_id) {
$perms = [];
@@ -16,16 +40,16 @@ class Permcat {
$role = get_pconfig($channel_id,'system','permissions_role');
if($role) {
- $x = Zaccess\PermissionRoles::role_perms($role);
+ $x = PermissionRoles::role_perms($role);
if($x['perms_connect']) {
- $perms = Zaccess\Permissions::FilledPerms($x['perms_connect']);
+ $perms = Permissions::FilledPerms($x['perms_connect']);
}
}
// if no role perms it may be a custom role, see if there any autoperms
if(! $perms) {
- $perms = Zaccess\Permissions::FilledAutoPerms($channel_id);
+ $perms = Permissions::FilledAutoPerms($channel_id);
}
// if no autoperms it may be a custom role with manual perms
@@ -50,13 +74,13 @@ class Permcat {
// nothing was found - create a filled permission array where all permissions are 0
if(! $perms) {
- $perms = Zaccess\Permissions::FilledPerms([]);
+ $perms = Permissions::FilledPerms([]);
}
$this->permcats[] = [
'name' => 'default',
'localname' => t('default','permcat'),
- 'perms' => Zaccess\Permissions::Operms($perms),
+ 'perms' => Permissions::Operms($perms),
'system' => 1
];
@@ -67,26 +91,39 @@ class Permcat {
$this->permcats[] = [
'name' => $p[$x][0],
'localname' => $p[$x][1],
- 'perms' => Zaccess\Permissions::Operms(Zaccess\Permissions::FilledPerms($p[$x][2])),
+ 'perms' => Permissions::Operms(Permissions::FilledPerms($p[$x][2])),
'system' => intval($p[$x][3])
];
}
}
}
-
+ /**
+ * @brief Return array with permcats.
+ *
+ * @return array
+ */
public function listing() {
return $this->permcats;
}
+ /**
+ * @brief
+ *
+ * @param string $name
+ * @return array
+ * * \e array with permcats
+ * * \e bool \b error if $name not found in permcats true
+ */
public function fetch($name) {
if($name && $this->permcats) {
foreach($this->permcats as $permcat) {
- if(strcasecmp($permcat['name'],$name) === 0) {
+ if(strcasecmp($permcat['name'], $name) === 0) {
return $permcat;
}
}
}
+
return ['error' => true];
}
@@ -118,29 +155,32 @@ class Permcat {
$permcats[] = [ $xv['k'], $xv['k'], $value, 0 ];
}
}
- }
+ }
- call_hooks('permcats',$permcats);
+ /**
+ * @hooks permcats
+ * * \e array
+ */
+ call_hooks('permcats', $permcats);
return $permcats;
-
}
- static public function find_permcat($arr,$name) {
+ static public function find_permcat($arr, $name) {
if((! $arr) || (! $name))
return false;
+
foreach($arr as $p)
if($p['name'] == $name)
return $p['value'];
}
- static public function update($channel_id, $name,$permarr) {
- PConfig::Set($channel_id,'permcat',$name,$permarr);
+ static public function update($channel_id, $name, $permarr) {
+ PConfig::Set($channel_id, 'permcat', $name, $permarr);
}
- static public function delete($channel_id,$name) {
- PConfig::Delete($channel_id,'permcat',$name);
+ static public function delete($channel_id, $name) {
+ PConfig::Delete($channel_id, 'permcat', $name);
}
-
} \ No newline at end of file
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index 67a507025..748edcdb7 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -38,7 +38,7 @@ class ThreadItem {
$this->toplevel = ($this->get_id() == $this->get_data_value('parent'));
// Prepare the children
- if(count($data['children'])) {
+ if($data['children']) {
foreach($data['children'] as $item) {
/*
@@ -105,7 +105,17 @@ class ThreadItem {
$mode = $conv->get_mode();
- $edlink = (($item['item_type'] == ITEM_TYPE_CARD) ? 'card_edit' : 'editpost');
+ switch($item['item_type']) {
+ case ITEM_TYPE_CARD:
+ $edlink = 'card_edit';
+ break;
+ case ITEM_TYPE_ARTICLE:
+ $edlink = 'article_edit';
+ break;
+ default:
+ $edlink = 'editpost';
+ break;
+ }
if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
$edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit'));
@@ -186,7 +196,7 @@ class ThreadItem {
$like_count = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid']] : '');
$like_list = ((x($conv_responses['like'],$item['mid'])) ? $conv_responses['like'][$item['mid'] . '-l'] : '');
- if (count($like_list) > MAX_LIKERS) {
+ if (($like_list) && (count($like_list) > MAX_LIKERS)) {
$like_list_part = array_slice($like_list, 0, MAX_LIKERS);
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 {
@@ -198,7 +208,7 @@ class ThreadItem {
$dislike_count = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid']] : '');
$dislike_list = ((x($conv_responses['dislike'],$item['mid'])) ? $conv_responses['dislike'][$item['mid'] . '-l'] : '');
$dislike_button_label = tt('Dislike','Dislikes',$dislike_count,'noun');
- if (count($dislike_list) > MAX_LIKERS) {
+ if (($dislike_list) && (count($dislike_list) > MAX_LIKERS)) {
$dislike_list_part = array_slice($dislike_list, 0, MAX_LIKERS);
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 {
@@ -303,7 +313,7 @@ class ThreadItem {
$comment_count_txt = sprintf( tt('%d comment','%d comments',$total_children),$total_children );
$list_unseen_txt = (($unseen_comments) ? sprintf('%d unseen',$unseen_comments) : '');
-
+
@@ -360,6 +370,7 @@ class ThreadItem {
'unverified' => $unverified,
'forged' => $forged,
'location' => $location,
+ 'divider' => get_pconfig($conv->get_profile_owner(),'system','item_divider'),
'attend_label' => t('Attend'),
'attend_title' => t('Attendance Options'),
'vote_label' => t('Vote'),
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index 436723f8c..d0c964149 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -54,6 +54,14 @@ class ThreadStream {
$this->profile_owner = local_channel();
$this->writable = true;
break;
+ case 'pubstream':
+ $this->profile_owner = local_channel();
+ $this->writable = ((local_channel()) ? true : false);
+ break;
+ case 'hq':
+ $this->profile_owner = local_channel();
+ $this->writable = true;
+ break;
case 'channel':
$this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
@@ -63,6 +71,11 @@ class ThreadStream {
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
$this->reload = $_SESSION['return_url'];
break;
+ case 'articles':
+ $this->profile_owner = \App::$profile['profile_uid'];
+ $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
+ $this->reload = $_SESSION['return_url'];
+ break;
case 'display':
// in this mode we set profile_owner after initialisation (from conversation()) and then
// pull some trickery which allows us to re-invoke this function afterward
@@ -179,6 +192,10 @@ class ThreadStream {
$item->set_commentable(can_comment_on_post($ob_hash,$item->data));
}
}
+ if($this->mode === 'pubstream' && (! local_channel())) {
+ $item->set_commentable(false);
+ }
+
require_once('include/channel.php');
$item->set_conversation($this);