aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Manning <andrewmanning@grid.reticu.li>2017-08-24 18:57:52 +0000
committerAndrew Manning <andrewmanning@grid.reticu.li>2017-08-24 18:57:52 +0000
commit90ec3340e44a3d3c69779f0db33ee49e864f326d (patch)
tree2b0bc859c3a7d31adcd1c9777d9868b9dbecdeda
parentcf2609530fcffdc7f5477336232b7cfde8b6403f (diff)
parentcdccf601320ec7e59f5d96438edac34d5ea30085 (diff)
downloadvolse-hubzilla-90ec3340e44a3d3c69779f0db33ee49e864f326d.tar.gz
volse-hubzilla-90ec3340e44a3d3c69779f0db33ee49e864f326d.tar.bz2
volse-hubzilla-90ec3340e44a3d3c69779f0db33ee49e864f326d.zip
Merge remote-tracking branch 'upstream/dev' into doco
-rw-r--r--Zotlabs/Daemon/Importfile.php47
-rw-r--r--Zotlabs/Daemon/Notifier.php2
-rw-r--r--Zotlabs/Lib/Apps.php1
-rw-r--r--Zotlabs/Lib/ThreadItem.php16
-rw-r--r--Zotlabs/Lib/ThreadStream.php6
-rw-r--r--Zotlabs/Module/Card_edit.php136
-rw-r--r--Zotlabs/Module/Cards.php185
-rw-r--r--Zotlabs/Module/Channel.php7
-rw-r--r--Zotlabs/Module/Display.php19
-rw-r--r--Zotlabs/Module/Item.php4
-rw-r--r--Zotlabs/Module/Linkinfo.php2
-rw-r--r--Zotlabs/Module/Oep.php114
-rw-r--r--Zotlabs/Module/Photos.php9
-rw-r--r--Zotlabs/Module/Profile.php14
-rw-r--r--Zotlabs/Module/Tasks.php3
-rw-r--r--Zotlabs/Module/Update_cards.php39
-rw-r--r--Zotlabs/Widget/Categories.php13
-rw-r--r--app/cards.apd6
-rwxr-xr-xboot.php6
-rw-r--r--doc/admin/administrator_guide.md13
-rw-r--r--include/attach.php8
-rw-r--r--include/contact_widgets.php43
-rw-r--r--include/conversation.php46
-rw-r--r--include/features.php9
-rwxr-xr-xinclude/items.php2
-rw-r--r--include/nav.php12
-rw-r--r--include/taxonomy.php76
-rw-r--r--include/zot.php5
-rwxr-xr-xutil/dcp105
-rw-r--r--view/js/main.js3
-rw-r--r--view/js/mod_cards.js3
-rw-r--r--view/pdl/mod_cards.pdl5
-rw-r--r--view/tpl/cards.tpl9
33 files changed, 904 insertions, 64 deletions
diff --git a/Zotlabs/Daemon/Importfile.php b/Zotlabs/Daemon/Importfile.php
new file mode 100644
index 000000000..c68ed21cf
--- /dev/null
+++ b/Zotlabs/Daemon/Importfile.php
@@ -0,0 +1,47 @@
+<?php /** @file */
+
+namespace Zotlabs\Daemon;
+
+class Importfile {
+
+ static public function run($argc,$argv){
+
+ logger('Importfile: ' . print_r($argv,true));
+
+ if($argc < 3)
+ return;
+
+ $channel = channelx_by_n($argv[1]);
+ if(! $channel)
+ return;
+
+ $srcfile = $argv[2];
+ $folder = (($argc > 3) ? $argv[3] : '');
+ $dstname = (($argc > 4) ? $argv[4] : '');
+
+ $hash = random_string();
+
+ $arr = [
+ 'src' => $srcfile,
+ 'filename' => (($dstname) ? $dstname : basename($srcfile)),
+ 'hash' => $hash,
+ 'allow_cid' => $channel['channel_allow_cid'],
+ 'allow_gid' => $channel['channel_allow_gid'],
+ 'deny_cid' => $channel['channel_deny_cid'],
+ 'deny_gid' => $channel['channel_deny_gid'],
+ 'preserve_original' => true,
+ 'replace' => true
+ ];
+
+ if($folder)
+ $arr['folder'] = $folder;
+
+ attach_store($channel,$channel['channel_hash'],'import',$arr);
+
+ $sync = attach_export_data($channel,$hash);
+ if($sync)
+ build_sync_packet($channel['channel_id'],array('file' => array($sync)));
+
+ return;
+ }
+}
diff --git a/Zotlabs/Daemon/Notifier.php b/Zotlabs/Daemon/Notifier.php
index 6763e0ef4..e8cd4dac8 100644
--- a/Zotlabs/Daemon/Notifier.php
+++ b/Zotlabs/Daemon/Notifier.php
@@ -276,7 +276,7 @@ class Notifier {
$deleted_item = true;
}
- if(intval($target_item['item_type']) != ITEM_TYPE_POST) {
+ if(! in_array(intval($target_item['item_type']), [ ITEM_TYPE_POST ] )) {
logger('notifier: target item not forwardable: type ' . $target_item['item_type'], LOGGER_DEBUG);
return;
}
diff --git a/Zotlabs/Lib/Apps.php b/Zotlabs/Lib/Apps.php
index 3730f3a23..37cbf9497 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'),
+ 'Cards' => t('Cards'),
'Site Admin' => t('Site Admin'),
'Report Bug' => t('Report Bug'),
'View Bookmarks' => t('View Bookmarks'),
diff --git a/Zotlabs/Lib/ThreadItem.php b/Zotlabs/Lib/ThreadItem.php
index ad944203c..313001cc7 100644
--- a/Zotlabs/Lib/ThreadItem.php
+++ b/Zotlabs/Lib/ThreadItem.php
@@ -29,6 +29,7 @@ class ThreadItem {
private $visiting = false;
private $channel = null;
private $display_mode = 'normal';
+ private $reload = '';
public function __construct($data) {
@@ -101,10 +102,13 @@ class ThreadItem {
if($item['author']['xchan_network'] === 'rss')
$shareable = true;
+
$mode = $conv->get_mode();
+ $edlink = (($item['item_type'] == ITEM_TYPE_CARD) ? 'card_edit' : 'editpost');
+
if(local_channel() && $observer['xchan_hash'] === $item['author_xchan'])
- $edpost = array(z_root()."/editpost/".$item['id'], t("Edit"));
+ $edpost = array(z_root() . '/' . $edlink . '/' . $item['id'], t('Edit'));
else
$edpost = false;
@@ -480,6 +484,14 @@ class ThreadItem {
return $this->threaded;
}
+ public function set_reload($val) {
+ $this->reload = $val;
+ }
+
+ public function get_reload() {
+ return $this->reload;
+ }
+
public function set_commentable($val) {
$this->commentable = $val;
foreach($this->get_children() as $child)
@@ -716,7 +728,7 @@ class ThreadItem {
$comment_box = replace_macros($template,array(
'$return_path' => '',
'$threaded' => $this->is_threaded(),
- '$jsreload' => '', //(($conv->get_mode() === 'display') ? $_SESSION['return_url'] : ''),
+ '$jsreload' => $conv->reload,
'$type' => (($conv->get_mode() === 'channel') ? 'wall-comment' : 'net-comment'),
'$id' => $this->get_id(),
'$parent' => $this->get_id(),
diff --git a/Zotlabs/Lib/ThreadStream.php b/Zotlabs/Lib/ThreadStream.php
index 0465b20ef..d7a898704 100644
--- a/Zotlabs/Lib/ThreadStream.php
+++ b/Zotlabs/Lib/ThreadStream.php
@@ -22,6 +22,7 @@ class ThreadStream {
private $profile_owner = 0;
private $preview = false;
private $prepared_item = '';
+ public $reload = '';
private $cipher = 'aes256';
// $prepared_item is for use by alternate conversation structures such as photos
@@ -57,6 +58,11 @@ class ThreadStream {
$this->profile_owner = \App::$profile['profile_uid'];
$this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
break;
+ case 'cards':
+ $this->profile_owner = \App::$profile['profile_uid'];
+ $this->writable = perm_is_allowed($this->profile_owner,$ob_hash,'post_comments');
+ $this->reload = $_SESSION['return_url'];
+ break;
case '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
diff --git a/Zotlabs/Module/Card_edit.php b/Zotlabs/Module/Card_edit.php
new file mode 100644
index 000000000..b5b44c567
--- /dev/null
+++ b/Zotlabs/Module/Card_edit.php
@@ -0,0 +1,136 @@
+<?php
+namespace Zotlabs\Module;
+
+require_once('include/channel.php');
+require_once('include/acl_selectors.php');
+require_once('include/conversation.php');
+
+class Card_edit extends \Zotlabs\Web\Controller {
+
+
+ function get() {
+
+ // Figure out which post we're editing
+ $post_id = ((argc() > 1) ? intval(argv(1)) : 0);
+
+ if(! $post_id) {
+ notice( t('Item not found') . EOL);
+ return;
+ }
+
+ $itm = q("SELECT * FROM item WHERE id = %d and item_type = %d LIMIT 1",
+ intval($post_id),
+ intval(ITEM_TYPE_CARD)
+ );
+ if($itm) {
+ $item_id = q("select * from iconfig where cat = 'system' and k = 'CARD' and iid = %d limit 1",
+ intval($itm[0]['id'])
+ );
+ if($item_id)
+ $card_title = $item_id[0]['v'];
+ }
+ else {
+ notice( t('Item not found') . EOL);
+ return;
+ }
+
+ $owner = $itm[0]['uid'];
+ $uid = local_channel();
+
+ $observer = \App::get_observer();
+
+ $channel = channelx_by_n($owner);
+ if(! $channel) {
+ notice( t('Channel not found.') . EOL);
+ return;
+ }
+
+ $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
+
+ if(! perm_is_allowed($owner,$ob_hash,'write_pages')) {
+ notice( t('Permission denied.') . EOL);
+ return;
+ }
+
+ $is_owner = (($uid && $uid == $owner) ? true : false);
+
+ $o = '';
+
+
+
+ $category = '';
+ $catsenabled = ((feature_enabled($owner,'categories')) ? 'categories' : '');
+
+ if ($catsenabled){
+ $itm = fetch_post_tags($itm);
+
+ $cats = get_terms_oftype($itm[0]['term'], TERM_CATEGORY);
+
+ foreach ($cats as $cat) {
+ if (strlen($category))
+ $category .= ', ';
+ $category .= $cat['term'];
+ }
+ }
+
+ if($itm[0]['attach']) {
+ $j = json_decode($itm[0]['attach'],true);
+ if($j) {
+ foreach($j as $jj) {
+ $itm[0]['body'] .= "\n" . '[attachment]' . basename($jj['href']) . ',' . $jj['revision'] . '[/attachment]' . "\n";
+ }
+ }
+ }
+
+
+ $mimetype = $itm[0]['mimetype'];
+
+ $content = $itm[0]['body'];
+
+
+
+ $rp = 'cards/' . $channel['channel_address'];
+
+ $x = array(
+ 'nickname' => $channel['channel_address'],
+ 'bbco_autocomplete'=> 'bbcode',
+ 'return_path' => $rp,
+ 'webpage' => ITEM_TYPE_CARD,
+ 'button' => t('Edit'),
+ 'writefiles' => perm_is_allowed($owner, get_observer_hash(), 'write_pages'),
+ 'weblink' => t('Insert web link'),
+ 'hide_voting' => false,
+ 'hide_future' => false,
+ 'hide_location' => false,
+ 'hide_expire' => false,
+ 'showacl' => true,
+ 'acl' => populate_acl($itm[0],false,\Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')),
+ 'ptyp' => $itm[0]['type'],
+ 'mimeselect' => false,
+ 'mimetype' => $itm[0]['mimetype'],
+ 'body' => undo_post_tagging($content),
+ 'post_id' => $post_id,
+ 'visitor' => true,
+ 'title' => htmlspecialchars($itm[0]['title'],ENT_COMPAT,'UTF-8'),
+ 'placeholdertitle' => t('Title (optional)'),
+ 'pagetitle' => $card_title,
+ 'profile_uid' => (intval($channel['channel_id'])),
+ 'catsenabled' => $catsenabled,
+ 'category' => $category,
+ 'bbcode' => (($mimetype == 'text/bbcode') ? true : false)
+ );
+
+ $editor = status_editor($a, $x);
+
+ $o .= replace_macros(get_markup_template('edpost_head.tpl'), array(
+ '$title' => t('Edit Block'),
+ '$delete' => ((($itm[0]['author_xchan'] === $ob_hash) || ($itm[0]['owner_xchan'] === $ob_hash)) ? t('Delete') : false),
+ '$id' => $itm[0]['id'],
+ '$editor' => $editor
+ ));
+
+ return $o;
+
+ }
+
+}
diff --git a/Zotlabs/Module/Cards.php b/Zotlabs/Module/Cards.php
new file mode 100644
index 000000000..9114e3b89
--- /dev/null
+++ b/Zotlabs/Module/Cards.php
@@ -0,0 +1,185 @@
+<?php
+namespace Zotlabs\Module;
+
+require_once('include/channel.php');
+require_once('include/conversation.php');
+require_once('include/acl_selectors.php');
+
+
+class Cards extends \Zotlabs\Web\Controller {
+
+ function init() {
+
+ if(argc() > 1)
+ $which = argv(1);
+ else
+ return;
+
+ profile_load($which);
+
+ }
+
+ function get($update = 0, $load = false) {
+
+ if(observer_prohibited(true)) {
+ return login();
+ }
+
+ if(! \App::$profile) {
+ notice( t('Requested profile is not available.') . EOL );
+ \App::$error = 404;
+ return;
+ }
+
+ if(! feature_enabled(\App::$profile_uid,'cards')) {
+ return;
+ }
+
+ nav_set_selected(t('Cards'));
+
+ head_add_link([
+ 'rel' => 'alternate',
+ 'type' => 'application/json+oembed',
+ 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
+ 'title' => 'oembed'
+ ]);
+
+
+ $category = (($_REQUEST['cat']) ? escape_tags(trim($_REQUEST['cat'])) : '');
+
+ if($category) {
+ $sql_extra2 .= protect_sprintf(term_item_parent_query(\App::$profile['profile_uid'],'item', $category, TERM_CATEGORY));
+ }
+
+
+ $which = argv(1);
+
+ $selected_card = ((argc() > 2) ? argv(2) : '');
+
+ $_SESSION['return_url'] = \App::$query_string;
+
+ $uid = local_channel();
+ $owner = \App::$profile_uid;
+ $observer = \App::get_observer();
+
+ $ob_hash = (($observer) ? $observer['xchan_hash'] : '');
+
+ if(! perm_is_allowed($owner,$ob_hash,'view_pages')) {
+ notice( t('Permission denied.') . EOL);
+ return;
+ }
+
+ $is_owner = ($uid && $uid == $owner);
+
+ $channel = channelx_by_n($owner);
+
+ if($channel) {
+ $channel_acl = array(
+ 'allow_cid' => $channel['channel_allow_cid'],
+ 'allow_gid' => $channel['channel_allow_gid'],
+ 'deny_cid' => $channel['channel_deny_cid'],
+ 'deny_gid' => $channel['channel_deny_gid']
+ );
+ }
+ else {
+ $channel_acl = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
+ }
+
+
+
+ if(perm_is_allowed($owner,$ob_hash,'write_pages')) {
+
+ $x = [
+ 'webpage' => ITEM_TYPE_CARD,
+ 'is_owner' => true,
+ 'nickname' => $channel['channel_address'],
+ 'lockstate' => (($channel['channel_allow_cid'] || $channel['channel_allow_gid']
+ || $channel['channel_deny_cid'] || $channel['channel_deny_gid']) ? 'lock' : 'unlock'),
+ 'acl' => (($is_owner) ? populate_acl($channel_acl, false,
+ \Zotlabs\Lib\PermissionDescription::fromGlobalPermission('view_pages')) : ''),
+ 'permissions' => $channel_acl,
+ 'showacl' => (($is_owner) ? true : false),
+ 'visitor' => true,
+ 'hide_location' => false,
+ 'hide_voting' => false,
+ 'profile_uid' => intval($owner),
+ 'mimetype' => 'text/bbcode',
+ 'mimeselect' => false,
+ 'layoutselect' => false,
+ 'expanded' => false,
+ 'novoting' => false,
+ 'catsenabled' => feature_enabled($owner,'categories'),
+ 'bbco_autocomplete' => 'bbcode',
+ 'bbcode' => true
+ ];
+
+ if($_REQUEST['title'])
+ $x['title'] = $_REQUEST['title'];
+ if($_REQUEST['body'])
+ $x['body'] = $_REQUEST['body'];
+ $editor = status_editor($a,$x);
+
+ }
+ else {
+ $editor = '';
+ }
+
+
+ $sql_extra = item_permissions_sql($owner);
+
+ if($selected_card) {
+ $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1",
+ dbesc($selected_card)
+ );
+ if($r) {
+ $sql_extra .= "and item.id = " . intval($r[0]['iid']) . " ";
+ }
+ }
+
+ $r = q("select * from item
+ where item.uid = %d and item_type = %d
+ $sql_extra order by item.created desc",
+ intval($owner),
+ intval(ITEM_TYPE_CARD)
+ );
+
+ $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0
+ and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
+ and item.item_blocked = 0 ";
+
+ if($r) {
+
+ $parents_str = ids_to_querystr($r,'id');
+
+ $items = q("SELECT item.*, item.id AS item_id
+ FROM item
+ WHERE item.uid = %d $item_normal
+ AND item.parent IN ( %s )
+ $sql_extra $sql_extra2 ",
+ intval(\App::$profile['profile_uid']),
+ dbesc($parents_str)
+ );
+ if($items) {
+ xchan_query($items);
+ $items = fetch_post_tags($items, true);
+ $items = conv_sort($items,'updated');
+ }
+ else
+ $items = [];
+ }
+
+ $mode = 'cards';
+
+ $content = conversation($items,$mode,false,'traditional');
+
+ $o = replace_macros(get_markup_template('cards.tpl'), [
+ '$title' => t('Cards'),
+ '$editor' => $editor,
+ '$content' => $content,
+ '$pager' => alt_pager($a,count($items))
+ ]);
+
+ return $o;
+ }
+
+}
diff --git a/Zotlabs/Module/Channel.php b/Zotlabs/Module/Channel.php
index c006d65d4..5e7db79d9 100644
--- a/Zotlabs/Module/Channel.php
+++ b/Zotlabs/Module/Channel.php
@@ -180,7 +180,12 @@ class Channel extends \Zotlabs\Web\Controller {
$simple_update = (($update) ? " AND item_unseen = 1 " : '');
- \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string) . '" title="oembed" />' . "\r\n";
+ head_add_link([
+ 'rel' => 'alternate',
+ 'type' => 'application/json+oembed',
+ 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
+ 'title' => 'oembed'
+ ]);
if($update && $_SESSION['loadtime'])
$simple_update = " AND (( item_unseen = 1 AND item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) OR item.changed > '" . datetime_convert('UTC','UTC',$_SESSION['loadtime']) . "' ) ";
diff --git a/Zotlabs/Module/Display.php b/Zotlabs/Module/Display.php
index d82ff2a55..b698513ba 100644
--- a/Zotlabs/Module/Display.php
+++ b/Zotlabs/Module/Display.php
@@ -39,7 +39,7 @@ class Display extends \Zotlabs\Web\Controller {
$observer_is_owner = false;
$updateable = false;
-
+
if(local_channel() && (! $update)) {
$channel = \App::get_channel();
@@ -174,8 +174,13 @@ class Display extends \Zotlabs\Web\Controller {
'$verb' => '',
'$mid' => $item_hash
));
-
-
+
+ head_add_link([
+ 'rel' => 'alternate',
+ 'type' => 'application/json+oembed',
+ 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
+ 'title' => 'oembed'
+ ]);
}
$observer_hash = get_observer_hash();
@@ -214,17 +219,17 @@ class Display extends \Zotlabs\Web\Controller {
if($r === null) {
// in case somebody turned off public access to sys channel content using permissions
- // make that content unsearchable by ensuring the owner_xchan can't match
+ // make that content unsearchable by ensuring the owner uid can't match
if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))
$sysid = 0;
-
+
$r = q("SELECT item.id as item_id from item
WHERE mid = '%s'
AND (((( item.allow_cid = '' AND item.allow_gid = '' AND item.deny_cid = ''
AND item.deny_gid = '' AND item_private = 0 )
- and owner_xchan in ( " . stream_perms_xchans(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " ))
+ and uid in ( " . stream_perms_api_uids(($observer_hash) ? (PERMS_NETWORK|PERMS_PUBLIC) : PERMS_PUBLIC) . " ))
OR uid = %d )
$sql_extra )
$item_normal
@@ -232,7 +237,7 @@ class Display extends \Zotlabs\Web\Controller {
dbesc($target_item['parent_mid']),
intval($sysid)
);
-
+
}
}
}
diff --git a/Zotlabs/Module/Item.php b/Zotlabs/Module/Item.php
index 5e7a3fbc0..797b46f93 100644
--- a/Zotlabs/Module/Item.php
+++ b/Zotlabs/Module/Item.php
@@ -735,6 +735,10 @@ class Item extends \Zotlabs\Web\Controller {
if($parent_item)
$parent_mid = $parent_item['mid'];
+ if($webpage == ITEM_TYPE_CARD && $pagetitle) {
+ $plink = z_root() . '/cards/' . $channel['channel_address'] . '/' . $pagetitle;
+ }
+
// Fallback so that we alway have a thr_parent
if(!$thr_parent)
diff --git a/Zotlabs/Module/Linkinfo.php b/Zotlabs/Module/Linkinfo.php
index 8f8231c49..78c34583e 100644
--- a/Zotlabs/Module/Linkinfo.php
+++ b/Zotlabs/Module/Linkinfo.php
@@ -95,7 +95,7 @@ class Linkinfo extends \Zotlabs\Web\Controller {
echo $arr['text'];
killme();
}
-
+
if($process_oembed) {
$x = oembed_process($url);
if($x) {
diff --git a/Zotlabs/Module/Oep.php b/Zotlabs/Module/Oep.php
index a3453ff31..02aa4aba9 100644
--- a/Zotlabs/Module/Oep.php
+++ b/Zotlabs/Module/Oep.php
@@ -41,6 +41,8 @@ class Oep extends \Zotlabs\Web\Controller {
$arr = $this->oep_profile_reply($_REQUEST);
elseif(fnmatch('*/profile/*',$url))
$arr = $this->oep_profile_reply($_REQUEST);
+ elseif(fnmatch('*/cards/*',$url))
+ $arr = $this->oep_cards_reply($_REQUEST);
if($arr) {
if($html) {
@@ -66,9 +68,9 @@ class Oep extends \Zotlabs\Web\Controller {
$url = $args['url'];
$maxwidth = intval($args['maxwidth']);
$maxheight = intval($args['maxheight']);
-
- if(preg_match('#//display/(.*?)(&|\?|$)#',$url,$matches)) {
- $res = $matches[1];
+ logger('processing display');
+ if(preg_match('#//(.*?)/display/(.*?)(&|\?|$)#',$url,$matches)) {
+ $res = $matches[2];
}
if(strpos($res,'b64.') === 0) {
@@ -78,7 +80,7 @@ class Oep extends \Zotlabs\Web\Controller {
$item_normal = item_normal();
$p = q("select * from item where mid like '%s' limit 1",
- dbesc($res . '%'),
+ dbesc($res . '%')
);
if(! $p)
@@ -89,7 +91,10 @@ class Oep extends \Zotlabs\Web\Controller {
if(! ($c && $res))
return;
-
+
+ if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream'))
+ return;
+
$sql_extra = item_permissions_sql($c['channel_id']);
$p = q("select * from item where mid like '%s' and uid = %d $sql_extra $item_normal limit 1",
@@ -140,6 +145,91 @@ class Oep extends \Zotlabs\Web\Controller {
return $ret;
}
+
+
+ function oep_cards_reply($args) {
+
+ $ret = [];
+ $url = $args['url'];
+ $maxwidth = intval($args['maxwidth']);
+ $maxheight = intval($args['maxheight']);
+
+ if(preg_match('#//(.*?)/cards/(.*?)/(.*?)(&|\?|$)#',$url,$matches)) {
+ $nick = $matches[2];
+ $res = $matches[3];
+ }
+ if(! ($nick && $res))
+ return $ret;
+
+ $channel = channelx_by_nick($nick);
+
+ if(! $channel)
+ return $ret;
+
+
+ if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages'))
+ return $ret;
+
+ $sql_extra = items_permissions_sql($channel['channel_id'],get_observer_hash());
+
+ $r = q("select * from iconfig where iconfig.cat = 'system' and iconfig.k = 'CARD' and iconfig.v = '%s' limit 1",
+ dbesc($res)
+ );
+ if($r) {
+ $sql_extra = "and item.id = " . intval($r[0]['iid']) . " ";
+ }
+ else {
+ return $ret;
+ }
+
+ $r = q("select * from item
+ where item.uid = %d and item_type = %d
+ $sql_extra order by item.created desc",
+ intval($channel['channel_id']),
+ intval(ITEM_TYPE_CARD)
+ );
+
+ $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0
+ and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
+ and item.item_blocked = 0 ";
+
+ if($r) {
+ xchan_query($r);
+ $p = fetch_post_tags($r, true);
+ }
+
+ $x = '2eGriplW^*Jmf4';
+
+
+ $o = "[share author='".urlencode($p[0]['author']['xchan_name']).
+ "' profile='".$p[0]['author']['xchan_url'] .
+ "' avatar='".$p[0]['author']['xchan_photo_s'].
+ "' link='".$p[0]['plink'].
+ "' posted='".$p[0]['created'].
+ "' message_id='".$p[0]['mid']."']";
+ if($p[0]['title'])
+ $o .= '[b]'.$p[0]['title'].'[/b]'."\r\n";
+
+ $o .= $x;
+ $o .= "[/share]";
+ $o = bbcode($o);
+
+ $o = str_replace($x,bbcode($p[0]['body']),$o);
+
+ $ret['type'] = 'rich';
+
+ $w = (($maxwidth) ? $maxwidth : 640);
+ $h = (($maxheight) ? $maxheight : intval($w * 2 / 3));
+
+ $ret['html'] = '<div style="width: ' . $w . '; height: ' . $h . '; font-family: sans-serif,arial,freesans;" >' . $o . '</div>';
+
+ $ret['width'] = $w;
+ $ret['height'] = $h;
+
+ return $ret;
+
+ }
+
function oep_mid_reply($args) {
@@ -161,6 +251,9 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $c)
return;
+
+ if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_stream'))
+ return;
$sql_extra = item_permissions_sql($c[0]['channel_id']);
@@ -279,6 +372,9 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $c)
return;
+ if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files'))
+ return;
+
$sql_extra = permissions_sql($c[0]['channel_id']);
$p = q("select resource_id from photo where album = '%s' and uid = %d and imgscale = 0 $sql_extra order by created desc limit 1",
@@ -340,6 +436,9 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $c)
return;
+ if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files'))
+ return;
+
$sql_extra = permissions_sql($c[0]['channel_id']);
$p = q("select resource_id from photo where uid = %d and imgscale = 0 $sql_extra order by created desc limit 1",
@@ -400,7 +499,10 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $c)
return;
-
+
+ if(! perm_is_allowed($c[0]['channel_id'],get_observer_hash(),'view_files'))
+ return;
+
$sql_extra = permissions_sql($c[0]['channel_id']);
diff --git a/Zotlabs/Module/Photos.php b/Zotlabs/Module/Photos.php
index 0f80f46a5..bb7079a6a 100644
--- a/Zotlabs/Module/Photos.php
+++ b/Zotlabs/Module/Photos.php
@@ -671,8 +671,13 @@ class Photos extends \Zotlabs\Web\Controller {
*/
if($datatype === 'album') {
-
- \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$cmd) . '" title="oembed" />' . "\r\n";
+
+ head_add_link([
+ 'rel' => 'alternate',
+ 'type' => 'application/json+oembed',
+ 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
+ 'title' => 'oembed'
+ ]);
if($x = photos_album_exists($owner_uid, get_observer_hash(), $datum)) {
\App::set_pager_itemspage(60);
diff --git a/Zotlabs/Module/Profile.php b/Zotlabs/Module/Profile.php
index 6930d50ca..43106e3af 100644
--- a/Zotlabs/Module/Profile.php
+++ b/Zotlabs/Module/Profile.php
@@ -94,7 +94,6 @@ class Profile extends \Zotlabs\Web\Controller {
echo \App::$profile['profile_vcard'];
killme();
}
-
$is_owner = ((local_channel()) && (local_channel() == \App::$profile['profile_uid']) ? true : false);
@@ -102,11 +101,14 @@ class Profile extends \Zotlabs\Web\Controller {
notice( t('Permission denied.') . EOL);
return;
}
-
- //$o .= profile_tabs($a, $is_owner, \App::$profile['channel_address']);
-
- \App::$page['htmlhead'] .= "\r\n" . '<link rel="alternate" type="application/json+oembed" href="' . z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string) . '" title="oembed" />' . "\r\n";
-
+
+ head_add_link([
+ 'rel' => 'alternate',
+ 'type' => 'application/json+oembed',
+ 'href' => z_root() . '/oep?f=&url=' . urlencode(z_root() . '/' . \App::$query_string),
+ 'title' => 'oembed'
+ ]);
+
$o .= advanced_profile($a);
call_hooks('profile_advanced',$o);
return $o;
diff --git a/Zotlabs/Module/Tasks.php b/Zotlabs/Module/Tasks.php
index c8deb11bf..0709f31f6 100644
--- a/Zotlabs/Module/Tasks.php
+++ b/Zotlabs/Module/Tasks.php
@@ -19,8 +19,8 @@ class Tasks extends \Zotlabs\Web\Controller {
$arr['all'] = 1;
$x = tasks_fetch($arr);
+ $x['html'] = '';
if($x['tasks']) {
- $x['html'] = '';
foreach($x['tasks'] as $y) {
$x['html'] .= '<div class="tasklist-item"><input type="checkbox" onchange="taskComplete(' . $y['id'] . '); return false;" /> ' . $y['summary'] . '</div>';
}
@@ -69,6 +69,7 @@ class Tasks extends \Zotlabs\Web\Controller {
if($x)
$ret['success'] = true;
}
+
json_return_and_die($ret);
}
diff --git a/Zotlabs/Module/Update_cards.php b/Zotlabs/Module/Update_cards.php
new file mode 100644
index 000000000..bb87357e8
--- /dev/null
+++ b/Zotlabs/Module/Update_cards.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Zotlabs\Module;
+
+/**
+ * Module: update_profile
+ * Purpose: AJAX synchronisation of profile page
+ *
+ */
+
+
+class Update_cards extends \Zotlabs\Web\Controller {
+
+function get() {
+
+ $profile_uid = intval($_GET['p']);
+ $load = (((argc() > 1) && (argv(1) == 'load')) ? 1 : 0);
+
+ header("Content-type: text/html");
+ echo "<!DOCTYPE html><html><body><section></section></body></html>\r\n";
+
+ killme();
+
+
+ $mod = new Cards();
+
+ $text = $mod->get($profile_uid,$load);
+
+ /**
+ * reportedly some versions of MSIE don't handle tabs in XMLHttpRequest documents very well
+ */
+
+ echo str_replace("\t",' ',$text);
+ echo (($_GET['msie'] == 1) ? '</div>' : '</section>');
+ echo "</body></html>\r\n";
+ killme();
+
+}
+}
diff --git a/Zotlabs/Widget/Categories.php b/Zotlabs/Widget/Categories.php
index d1dcfda93..84deb03d6 100644
--- a/Zotlabs/Widget/Categories.php
+++ b/Zotlabs/Widget/Categories.php
@@ -8,8 +8,13 @@ class Categories {
function widget($arr) {
+ $cards = ((array_key_exists('cards',$arr) && $arr['cards']) ? true : false);
+
+ if(($cards) && (! feature_enabled(\App::$profile['profile_uid'],'cards')))
+ return '';
+
if((! \App::$profile['profile_uid'])
- || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),'view_stream'))) {
+ || (! perm_is_allowed(\App::$profile['profile_uid'],get_observer_hash(),(($cards) ? 'view_pages' : 'view_stream')))) {
return '';
}
@@ -18,6 +23,10 @@ class Categories {
$srchurl = rtrim(preg_replace('/cat\=[^\&].*?(\&|$)/is','',$srchurl),'&');
$srchurl = str_replace(array('?f=','&f='),array('',''),$srchurl);
- return categories_widget($srchurl, $cat);
+ if($cards)
+ return cardcategories_widget($srchurl, $cat);
+ else
+ return categories_widget($srchurl, $cat);
+
}
}
diff --git a/app/cards.apd b/app/cards.apd
new file mode 100644
index 000000000..047aaeac9
--- /dev/null
+++ b/app/cards.apd
@@ -0,0 +1,6 @@
+version: 1.1
+url: $baseurl/cards/$nick
+name: Cards
+requires: local_channel, cards
+photo: icon:list
+categories: Productivity
diff --git a/boot.php b/boot.php
index 419efbac2..d9a6cd230 100755
--- a/boot.php
+++ b/boot.php
@@ -256,7 +256,7 @@ define ( 'NETWORK_OSTATUS', 'stat'); // status.net, identi.ca, GNU-s
define ( 'NETWORK_GNUSOCIAL', 'gnusoc'); // status.net, identi.ca, GNU-social, other OStatus implementations
define ( 'NETWORK_FEED', 'rss'); // RSS/Atom feeds with no known "post/notify" protocol
define ( 'NETWORK_DIASPORA', 'diaspora'); // Diaspora
-define ( 'NETWORK_ACTIVITYPUB', 'activitypub');
+define ( 'NETWORK_ACTIVITYPUB', 'activitypub');
define ( 'NETWORK_MAIL', 'mail'); // IMAP/POP
define ( 'NETWORK_MAIL2', 'mai2'); // extended IMAP/POP
define ( 'NETWORK_FACEBOOK', 'face'); // Facebook API
@@ -509,6 +509,7 @@ define ( 'ACTIVITY_OBJ_PROFILE', NAMESPACE_ZOT . '/activity/profile' );
define ( 'ACTIVITY_OBJ_THING', NAMESPACE_ZOT . '/activity/thing' );
define ( 'ACTIVITY_OBJ_LOCATION',NAMESPACE_ZOT . '/activity/location' );
define ( 'ACTIVITY_OBJ_FILE', NAMESPACE_ZOT . '/activity/file' );
+define ( 'ACTIVITY_OBJ_CARD', NAMESPACE_ZOT . '/activity/card' );
/**
* Account Flags
@@ -547,6 +548,8 @@ define ( 'ITEM_PDL', 0x0200); // Page Description Language - e.g. Comanche
define ( 'ITEM_BUG', 0x0400); // Is a bug, can be used by the internal bug tracker
define ( 'ITEM_PENDING_REMOVE', 0x0800); // deleted, notification period has lapsed
define ( 'ITEM_DOC', 0x1000); // hubzilla only, define here so that item import does the right thing
+define ( 'ITEM_CARD', 0x2000);
+
define ( 'ITEM_TYPE_POST', 0 );
define ( 'ITEM_TYPE_BLOCK', 1 );
@@ -554,6 +557,7 @@ define ( 'ITEM_TYPE_PDL', 2 );
define ( 'ITEM_TYPE_WEBPAGE', 3 );
define ( 'ITEM_TYPE_BUG', 4 );
define ( 'ITEM_TYPE_DOC', 5 );
+define ( 'ITEM_TYPE_CARD', 6 );
define ( 'ITEM_IS_STICKY', 1000 );
diff --git a/doc/admin/administrator_guide.md b/doc/admin/administrator_guide.md
index 9e0ee3c59..e3b6190cb 100644
--- a/doc/admin/administrator_guide.md
+++ b/doc/admin/administrator_guide.md
@@ -346,6 +346,19 @@ empty:
1. After successful import (check!) delete your channel on the old RedMatrix Server.
1. On the $Projectname server visit new.hub/locs and upgrade to your channel to a primary one. And when the old Redmatrix server is still listed delete them here as well. Press "Sync" to inform all other server in the grid.
+### Administration
+
+#### Site Administration
+
+Administration of the website is commonly done through the admin webpage located at /admin on your website. In order to access this page you must have administration rights to the server. Administration rights are granted to the first account to register on your site, **provided** the email address of that account exactly matches the email address you provided as the administrator's email address during setup.
+
+There are several ways that this can fail and leave the system without an administrator account, for instance if the first account that was created provided a different email address than the administrator email address that was supplied during setup.
+
+For security reasons there is no web page or interface on the system which will give you administrator access. If you need to correct a situation where a system has no administrator account it **must** be done by editing the account table in the database. There is no other way. To do this, you will need to locate the entry in the account table which belongs to the desired administrator, and set 'account_roles' for that entry to 4096. You will then be able to access the admin page from your system's profile menu or directly via /admin .
+
+A hub can have multiple admins and there is no limit to how administrators you can have. Repeat the above process for every account you wish to provide with administration rights.
+
+
### Troubleshooting
#### Log files
diff --git a/include/attach.php b/include/attach.php
index 79a38590b..78e133b03 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -460,6 +460,7 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
// By default remove $src when finished
$remove_when_processed = true;
+ $import_replace = false;
if($options === 'import') {
$src = $arr['src'];
@@ -476,6 +477,9 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
if($arr['preserve_original'])
$remove_when_processed = false;
+ if($arr['replace'])
+ $import_replace = true;
+
// if importing a directory, just do it now and go home - we're done.
if(array_key_exists('is_dir',$arr) && intval($arr['is_dir'])) {
@@ -617,8 +621,10 @@ function attach_store($channel, $observer_hash, $options = '', $arr = null) {
dbesc($folder_hash)
);
if($r) {
- $overwrite = get_pconfig($channel_id,'system','overwrite_dup_files');
+ $overwrite = (($import_replace || get_pconfig($channel_id,'system','overwrite_dup_files')) ? true : false);
if(($overwrite) || ($options === 'import')) {
+ if(! array_key_exists('edited',$arr))
+ $arr['edited'] = datetime_convert();
$options = 'replace';
$existing_id = $x[0]['id'];
$existing_size = intval($x[0]['filesize']);
diff --git a/include/contact_widgets.php b/include/contact_widgets.php
index 8f76bb4bc..aac756a46 100644
--- a/include/contact_widgets.php
+++ b/include/contact_widgets.php
@@ -100,6 +100,49 @@ function categories_widget($baseurl,$selected = '') {
return '';
}
+function cardcategories_widget($baseurl,$selected = '') {
+
+ if(! feature_enabled(App::$profile['profile_uid'],'categories'))
+ return '';
+
+ $item_normal = "and item.item_hidden = 0 and item.item_type = 6 and item.item_deleted = 0
+ and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
+ and item.item_blocked = 0 ";
+
+ $terms = array();
+ $r = q("select distinct(term.term)
+ from term join item on term.oid = item.id
+ where item.uid = %d
+ and term.uid = item.uid
+ and term.ttype = %d
+ and term.otype = %d
+ and item.owner_xchan = '%s'
+ $item_normal
+ order by term.term asc",
+ intval(App::$profile['profile_uid']),
+ intval(TERM_CATEGORY),
+ intval(TERM_OBJ_POST),
+ dbesc(App::$profile['channel_hash'])
+ );
+ if($r && count($r)) {
+ foreach($r as $rr)
+ $terms[] = array('name' => $rr['term'], 'selected' => (($selected == $rr['term']) ? 'selected' : ''));
+
+ return replace_macros(get_markup_template('categories_widget.tpl'),array(
+ '$title' => t('Categories'),
+ '$desc' => '',
+ '$sel_all' => (($selected == '') ? 'selected' : ''),
+ '$all' => t('Everything'),
+ '$terms' => $terms,
+ '$base' => $baseurl,
+
+ ));
+ }
+ return '';
+}
+
+
+
function common_friends_visitor_widget($profile_uid) {
if(local_channel() == $profile_uid)
diff --git a/include/conversation.php b/include/conversation.php
index 3b8ec19d3..2d5ccb5b1 100644
--- a/include/conversation.php
+++ b/include/conversation.php
@@ -464,6 +464,7 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$profile_owner = 0;
$page_writeable = false;
$live_update_div = '';
+ $jsreload = '';
$preview = (($page_mode === 'preview') ? true : false);
$previewing = (($preview) ? ' preview ' : '');
@@ -517,6 +518,16 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
}
}
+ elseif ($mode === 'cards') {
+ $profile_owner = App::$profile['profile_uid'];
+ $page_writeable = ($profile_owner == local_channel());
+ $live_update_div = '<div id="live-cards"></div>' . "\r\n"
+ . "<script> var profile_uid = " . App::$profile['profile_uid']
+ . "; var netargs = '?f='; var profile_page = " . App::$pager['page'] . "; </script>\r\n";
+ $jsreload = $_SESSION['return_url'];
+ }
+
+
elseif ($mode === 'display') {
$profile_owner = local_channel();
$page_writeable = false;
@@ -808,6 +819,10 @@ function conversation($items, $mode, $update, $page_mode = 'traditional', $prepa
$item_object->set_template('conv_list.tpl');
$item_object->set_display_mode('list');
}
+ if($page_mode === 'cards') {
+ $item_object->set_reload($jsreload);
+ }
+
}
}
@@ -1305,6 +1320,11 @@ function status_editor($a, $x, $popup = false) {
if(! $cipher)
$cipher = 'aes256';
+ if(array_key_exists('catsenabled',$x))
+ $catsenabled = $x['catsenabled'];
+ else
+ $catsenabled = ((feature_enabled($x['profile_uid'], 'categories') && (! $webpage)) ? 'categories' : '');
+
// avoid illegal offset errors
if(! array_key_exists('permissions',$x))
$x['permissions'] = [ 'allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '' ];
@@ -1349,7 +1369,7 @@ function status_editor($a, $x, $popup = false) {
'$clearloc' => $clearloc,
'$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT,'UTF-8') : ''),
'$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')),
- '$catsenabled' => ((feature_enabled($x['profile_uid'], 'categories') && (! $webpage)) ? 'categories' : ''),
+ '$catsenabled' => $catsenabled,
'$category' => ((x($x, 'category')) ? $x['category'] : ''),
'$placeholdercategory' => t('Categories (optional, comma-separated list)'),
'$permset' => t('Permission settings'),
@@ -1456,6 +1476,8 @@ function conv_sort($arr, $order) {
usort($parents,'sort_thr_created');
elseif(stristr($order,'commented'))
usort($parents,'sort_thr_commented');
+ elseif(stristr($order,'updated'))
+ usort($parents,'sort_thr_updated');
elseif(stristr($order,'ascending'))
usort($parents,'sort_thr_created_rev');
@@ -1497,6 +1519,12 @@ function sort_thr_commented($a,$b) {
return strcmp($b['commented'],$a['commented']);
}
+function sort_thr_updated($a,$b) {
+ $indexa = (($a['changed'] > $a['edited']) ? $a['changed'] : $a['edited']);
+ $indexb = (($b['changed'] > $b['edited']) ? $b['changed'] : $b['edited']);
+ return strcmp($indexb,$indexa);
+}
+
function find_thread_parent_index($arr,$x) {
foreach($arr as $k => $v)
if($v['id'] == $x['parent'])
@@ -1835,7 +1863,8 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
require_once('include/menu.php');
$has_bookmarks = menu_list_count(local_channel(),'',MENU_BOOKMARK) + menu_list_count(local_channel(),'',MENU_SYSTEM|MENU_BOOKMARK);
- if ($is_owner && $has_bookmarks) {
+
+ if($is_owner && $has_bookmarks) {
$tabs[] = array(
'label' => t('Bookmarks'),
'url' => z_root() . '/bookmarks',
@@ -1846,6 +1875,17 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
);
}
+ if(feature_enabled($uid,'cards')) {
+ $tabs[] = array(
+ 'label' => t('Cards'),
+ 'url' => z_root() . '/cards/' . $nickname,
+ 'sel' => ((argv(0) == 'cards') ? 'active' : ''),
+ 'title' => t('View Cards'),
+ 'id' => 'cards-tab',
+ 'icon' => 'list'
+ );
+ }
+
if($has_webpages && feature_enabled($uid,'webpages')) {
$tabs[] = array(
'label' => t('Webpages'),
@@ -1856,7 +1896,7 @@ function profile_tabs($a, $is_owner = false, $nickname = null){
'icon' => 'newspaper-o'
);
}
-
+
if ($p['view_wiki']) {
if(feature_enabled($uid,'wiki') && (get_account_techlevel($account_id) > 3)) {
diff --git a/include/features.php b/include/features.php
index f32dbe82e..f84c9cb05 100644
--- a/include/features.php
+++ b/include/features.php
@@ -118,6 +118,15 @@ function get_features($filtered = true) {
],
[
+ 'cards',
+ t('Cards'),
+ t('Create personal planning cards'),
+ false,
+ get_config('feature_lock','cards'),
+ feature_level('cards',1),
+ ],
+
+ [
'nav_channel_select',
t('Navigation Channel Select'),
t('Change channels directly from within the navigation dropdown menu'),
diff --git a/include/items.php b/include/items.php
index e7bec7f20..070c4571a 100755
--- a/include/items.php
+++ b/include/items.php
@@ -4111,6 +4111,8 @@ function webpage_to_namespace($webpage) {
$page_type = 'BUILDBLOCK';
elseif($webpage == ITEM_TYPE_PDL)
$page_type = 'PDL';
+ elseif($webpage == ITEM_TYPE_CARD)
+ $page_type = 'CARD';
elseif($webpage == ITEM_TYPE_DOC)
$page_type = 'docfile';
else
diff --git a/include/nav.php b/include/nav.php
index 76a8f8fb9..f5a1236a8 100644
--- a/include/nav.php
+++ b/include/nav.php
@@ -434,6 +434,18 @@ function channel_apps($is_owner = false, $nickname = null) {
];
}
+ if($p['view_pages'] && feature_enabled($uid,'cards')) {
+ $tabs[] = [
+ 'label' => t('Cards'),
+ 'url' => z_root() . '/cards/' . $nickname ,
+ 'sel' => ((argv(0) == 'cards') ? 'active' : ''),
+ 'title' => t('View Cards'),
+ 'id' => 'cards-tab',
+ 'icon' => 'list'
+ ];
+ }
+
+
if($has_webpages && feature_enabled($uid,'webpages')) {
$tabs[] = [
'label' => t('Webpages'),
diff --git a/include/taxonomy.php b/include/taxonomy.php
index 46d661581..36e05b008 100644
--- a/include/taxonomy.php
+++ b/include/taxonomy.php
@@ -199,6 +199,62 @@ function tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $re
}
+
+
+function card_tagadelic($uid, $count = 0, $authors = '', $owner = '', $flags = 0, $restrict = 0, $type = TERM_CATEGORY) {
+
+ require_once('include/security.php');
+
+ if(! perm_is_allowed($uid,get_observer_hash(),'view_pages'))
+ return array();
+
+
+ $item_normal = item_normal();
+ $sql_options = item_permissions_sql($uid);
+ $count = intval($count);
+
+ if($flags) {
+ if($flags === 'wall')
+ $sql_options .= " and item_wall = 1 ";
+ }
+
+ if($authors) {
+ if(! is_array($authors))
+ $authors = array($authors);
+
+ stringify_array_elms($authors,true);
+ $sql_options .= " and author_xchan in (" . implode(',',$authors) . ") ";
+ }
+
+ if($owner) {
+ $sql_options .= " and owner_xchan = '" . dbesc($owner) . "' ";
+ }
+
+
+ // Fetch tags
+ $r = q("select term, count(term) as total from term left join item on term.oid = item.id
+ where term.uid = %d and term.ttype = %d
+ and otype = %d and item_type = %d and item_private = 0
+ $sql_options $item_normal
+ group by term order by total desc %s",
+ intval($uid),
+ intval($type),
+ intval(TERM_OBJ_POST),
+ intval($restrict),
+ ((intval($count)) ? "limit $count" : '')
+ );
+
+ if(! $r)
+ return array();
+
+ return Zotlabs\Text\Tagadelic::calc($r);
+
+}
+
+
+
+
+
function dir_tagadelic($count = 0) {
$count = intval($count);
@@ -316,6 +372,26 @@ function catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restric
return $o;
}
+function card_catblock($uid,$count = 0,$authors = '',$owner = '', $flags = 0,$restrict = 0,$type = TERM_CATEGORY) {
+ $o = '';
+
+ $r = card_tagadelic($uid,$count,$authors,$owner,$flags,$restrict,$type);
+
+ if($r) {
+ $c = q("select channel_address from channel where channel_id = %d limit 1",
+ intval($uid)
+ );
+
+ $o = '<div class="tagblock widget"><h3>' . t('Categories') . '</h3><div class="tags" align="center">';
+ foreach($r as $rr) {
+ $o .= '<a href="cards/' . $c[0]['channel_address']. '?f=&cat=' . urlencode($rr[0]).'" class="tag'.$rr[2].'">'.$rr[0].'</a> ' . "\r\n";
+ }
+ $o .= '</div></div>';
+ }
+
+ return $o;
+}
+
function dir_tagblock($link,$r) {
$o = '';
diff --git a/include/zot.php b/include/zot.php
index 3e2a66734..343bc8ad8 100644
--- a/include/zot.php
+++ b/include/zot.php
@@ -2965,8 +2965,6 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
logger('build_sync_packet');
- if($packet)
- logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG);
$keychange = (($packet && array_key_exists('keychange',$packet)) ? true : false);
if($keychange) {
@@ -3033,6 +3031,9 @@ function build_sync_packet($uid = 0, $packet = null, $groups_changed = false) {
$env_recips = array();
$env_recips[] = array('guid' => $r[0]['xchan_guid'],'guid_sig' => $r[0]['xchan_guid_sig']);
+ if($packet)
+ logger('packet: ' . print_r($packet, true),LOGGER_DATA, LOG_DEBUG);
+
$info = (($packet) ? $packet : array());
$info['type'] = 'channel_sync';
$info['encoding'] = 'red'; // note: not zot, this packet is very platform specific
diff --git a/util/dcp b/util/dcp
index e38725aa1..2817ad4f1 100755
--- a/util/dcp
+++ b/util/dcp
@@ -15,15 +15,15 @@ cli_startup();
if($argc <= 3) {
- echo "Usage: " . $argv[0] . ' source destination' . "\n";
+ echo "Usage: " . $argv[0] . ' src dstdir' . "\n";
echo 'Always run from the toplevel web directory.' . "\n";
echo 'destination should begin with store/$nickname/desired/path or $nickname/desired/path' . "\n";
- echo 'Example: util/dcp /etc/motd store/joe/etc/motd' . "\n";
+ echo 'Example: util/dcp /etc/motd store/joe/etc' . "\n";
exit;
}
- $dstfile = $argv[$argc - 1];
-
+ $recursive = false;
+ $dstfile = $argv[$argc - 1];
if(strpos($dstfile,'store/') === 0)
$dstfile = substr($dstfile,6);
@@ -36,8 +36,20 @@ if($argc <= 3) {
if(! $channel)
return;
+ for($x = 1; $x < ($argc - 1); $x ++) {
+ if(($argv[$x] === '-r') || ($argv[$x] === '-R')) {
+ $recursive = true;
+ break;
+ }
+ }
+
+
$isadir = false;
+ if(($recursive) || ($argc > 3))
+ $isadir = true;
+
+
$r = q("select * from attach where display_path = '%s' and uid = %d limit 1",
dbesc($dstfile),
intval($channel['channel_id'])
@@ -45,11 +57,13 @@ if($argc <= 3) {
if($r && $r[0]['is_dir']) {
$isadir = true;
+ $basepath = $dstfile;
$folder = $r[0]['hash'];
}
else {
+ $pathname = (($isadir) ? $dstfile : dirname($dstfile));
$arr = [
- 'pathname' => dirname($dstfile),
+ 'pathname' => $pathname,
'allow_cid' => $channel['channel_allow_cid'],
'allow_gid' => $channel['channel_allow_gid'],
'deny_cid' => $channel['channel_deny_cid'],
@@ -57,36 +71,73 @@ if($argc <= 3) {
];
$folder = '';
- if(dirname($dstfile)) {
+ if($pathname && $isadir) {
$x = attach_mkdirp($channel,$channel['channel_hash'],$arr);
if($x['success'])
$folder = $x['data']['hash'];
}
}
-
for($x = 1; $x < ($argc - 1); $x ++) {
- $srcfile = $argv[$x];
-
- $hash = random_string();
+ if(($argv[$x] === '-r') || ($argv[$x] === '-R')) {
+ continue;
+ }
- $arr = [
- 'src' => $srcfile,
- 'filename' => basename($srcfile),
- 'hash' => $hash,
- 'allow_cid' => $channel['channel_allow_cid'],
- 'allow_gid' => $channel['channel_allow_gid'],
- 'deny_cid' => $channel['channel_deny_cid'],
- 'deny_gid' => $channel['channel_deny_gid'],
- 'preserve_original' => true,
- ];
+ if(is_dir($argv[$x])) {
+ if($recursive) {
+ dcp_recurse($channel,$argv[$x],$basepath,$folder);
+ }
+ else {
+ continue;
+ }
+ }
+ else {
+ $dstname = (($isadir) ? '' : basename($dstfile));
+ $cmd = [ 'Importfile', $channel['channel_id'], $argv[$x], $folder, $dstname ];
+ \Zotlabs\Daemon\Master::Summon($cmd);
+ }
+ }
- if($folder)
- $arr['folder'] = $folder;
-
- attach_store($channel,$channel['channel_hash'],'import',$arr);
- $sync = attach_export_data($channel,$hash);
- if($sync)
- build_sync_packet($channel['channel_id'],array('file' => array($sync)));
+ function dcp_recurse($channel,$src,$basepath,$folder) {
+ $dir = opendir($src);
+ if($dir) {
+ while(($entry = readdir($dir)) !== false) {
+ if($entry === '.' || $entry === '..')
+ continue;
+
+ $dstfile = $basepath . '/' . $entry;
+ if(is_dir($src . '/' . $entry)) {
+ $r = q("select * from attach where display_path = '%s' and uid = %d limit 1",
+ dbesc($dstfile),
+ intval($channel['channel_id'])
+ );
+
+ if($r && $r[0]['is_dir']) {
+ $folder = $r[0]['hash'];
+ }
+ else {
+
+ $arr = [
+ 'pathname' => $dstfile,
+ 'allow_cid' => $channel['channel_allow_cid'],
+ 'allow_gid' => $channel['channel_allow_gid'],
+ 'deny_cid' => $channel['channel_deny_cid'],
+ 'deny_gid' => $channel['channel_deny_gid'],
+ ];
+
+ $folder = '';
+ $x = attach_mkdirp($channel,$channel['channel_hash'],$arr);
+ if($x['success'])
+ $folder = $x['data']['hash'];
+ }
+ dcp_recurse($channel,$src . '/' . $entry,$dstfile,$folder);
+ }
+ else {
+ $cmd = [ 'Importfile', $channel['channel_id'], $src . '/' . $entry, $folder ];
+ \Zotlabs\Daemon\Master::Summon($cmd);
+ }
+ }
+ closedir($dir);
+ }
}
diff --git a/view/js/main.js b/view/js/main.js
index f20432735..fd3c8b08f 100644
--- a/view/js/main.js
+++ b/view/js/main.js
@@ -380,8 +380,9 @@ function NavUpdate() {
if($('#live-pubstream').length) { src = 'pubstream'; liveUpdate(); }
if($('#live-display').length) { src = 'display'; liveUpdate(); }
if($('#live-search').length) { src = 'search'; liveUpdate(); }
+ // if($('#live-cards').length) { src = 'cards'; liveUpdate(); }
- if($('#live-photos').length) {
+ if($('#live-photos').length || $('#live-cards').length) {
if(liking) {
liking = 0;
window.location.href=window.location.href;
diff --git a/view/js/mod_cards.js b/view/js/mod_cards.js
new file mode 100644
index 000000000..78b2e1bd0
--- /dev/null
+++ b/view/js/mod_cards.js
@@ -0,0 +1,3 @@
+$(document).ready( function() {
+ $(".autotime").timeago();
+}); \ No newline at end of file
diff --git a/view/pdl/mod_cards.pdl b/view/pdl/mod_cards.pdl
new file mode 100644
index 000000000..5546d97cb
--- /dev/null
+++ b/view/pdl/mod_cards.pdl
@@ -0,0 +1,5 @@
+[region=aside]
+[widget=categories][var=cards]1[/var][/widget]
+[widget=tasklist][/widget]
+[widget=notes][/widget]
+[/region]
diff --git a/view/tpl/cards.tpl b/view/tpl/cards.tpl
new file mode 100644
index 000000000..a06e2fd22
--- /dev/null
+++ b/view/tpl/cards.tpl
@@ -0,0 +1,9 @@
+<div class="generic-content-wrapper">
+ <div class="section-title-wrapper">
+ <h2>{{$title}}</h2>
+ </div>
+ <div id="live-cards"></div>
+ {{$editor}}
+ {{$content}}
+</div>
+ {{$pager}}