From c98b91f5149d53f522a19ec0f69b4d4727d34896 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Wed, 17 Aug 2016 18:19:36 -0400 Subject: Stash changes for merge with dev --- include/text.php | 22 ++++++++++++++++++++++ include/widgets.php | 15 +++++++++++++++ view/pdl/mod_webpages.pdl | 1 + view/tpl/website_export_tools.tpl | 14 ++++++++++++++ 4 files changed, 52 insertions(+) create mode 100644 view/tpl/website_export_tools.tpl diff --git a/include/text.php b/include/text.php index a2a6d918b..c8e0b1888 100644 --- a/include/text.php +++ b/include/text.php @@ -2294,6 +2294,28 @@ function website_import_tools() { )); } +/** + * @brief Creates website export tools menu + * + * @return string + */ +function website_export_tools() { + + $channel = App::get_channel(); + $sys = false; + + if(App::$is_sys && is_site_admin()) { + require_once('include/channel.php'); + $channel = get_sys_channel(); + $sys = true; + } + + return replace_macros(get_markup_template('website_export_tools.tpl'), array( + '$title' => t('Export'), + '$export_label' => t('Export website...'), + )); +} + /* case insensitive in_array() */ function in_arrayi($needle, $haystack) { diff --git a/include/widgets.php b/include/widgets.php index 68db74703..fc2a9830b 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -794,6 +794,21 @@ function widget_website_import_tools($arr) { return website_import_tools(); } +function widget_website_export_tools($arr) { + + // mod menu doesn't load a profile. For any modules which load a profile, check it. + // otherwise local_channel() is sufficient for permissions. + + if(App::$profile['profile_uid']) + if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) + return ''; + + if(! local_channel()) + return ''; + + return website_export_tools(); +} + function widget_findpeople($arr) { return findpeople_widget(); } diff --git a/view/pdl/mod_webpages.pdl b/view/pdl/mod_webpages.pdl index b62ec6e7c..292d6791c 100644 --- a/view/pdl/mod_webpages.pdl +++ b/view/pdl/mod_webpages.pdl @@ -1,4 +1,5 @@ [region=aside] [widget=design_tools][/widget] [widget=website_import_tools][/widget] +[widget=website_export_tools][/widget] [/region] \ No newline at end of file diff --git a/view/tpl/website_export_tools.tpl b/view/tpl/website_export_tools.tpl new file mode 100644 index 000000000..36f5c8e24 --- /dev/null +++ b/view/tpl/website_export_tools.tpl @@ -0,0 +1,14 @@ +
+

{{$title}}

+ +
-- cgit v1.2.3 From f2e87a204d0a257edb501dfedaad867700c58842 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Wed, 17 Aug 2016 21:25:50 -0400 Subject: Progress implementing zip file export --- Zotlabs/Module/Webpages.php | 26 ++++++++++++++-- include/text.php | 32 +++++--------------- include/widgets.php | 19 ++---------- view/pdl/mod_webpages.pdl | 3 +- view/tpl/webpage_export_list.tpl | 21 +++++++++++++ view/tpl/website_export_tools.tpl | 14 --------- view/tpl/website_import_tools.tpl | 37 ----------------------- view/tpl/website_portation_tools.tpl | 58 ++++++++++++++++++++++++++++++++++++ 8 files changed, 113 insertions(+), 97 deletions(-) create mode 100644 view/tpl/webpage_export_list.tpl delete mode 100644 view/tpl/website_export_tools.tpl delete mode 100644 view/tpl/website_import_tools.tpl create mode 100644 view/tpl/website_portation_tools.tpl diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 0a48d43c6..439d60a89 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -62,6 +62,16 @@ class Webpages extends \Zotlabs\Web\Controller { case 'importselected': $_SESSION['action'] = null; break; + case 'export_select_list': + $_SESSION['action'] = null; + $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( + '$title' => t('Export Webpage Elements'), + '$exportbtn' => t('Export selected'), + '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' + )); + $_SESSION['export'] = null; + return $o; + default : $_SESSION['action'] = null; break; @@ -233,7 +243,7 @@ class Webpages extends \Zotlabs\Web\Controller { } function post() { - + logger(json_encode($_REQUEST), LOGGER_DEBUG); $action = $_REQUEST['action']; if( $action ){ switch ($action) { @@ -382,7 +392,19 @@ class Webpages extends \Zotlabs\Web\Controller { info( t('Import complete.') . EOL); } break; - + + case 'exportzipfile': + + if(isset($_POST['w_download'])) { + logger($_POST['w_download'], LOGGER_DEBUG); + $_SESSION['action'] = 'export_select_list'; + $_SESSION['export'] = 'zipfile'; + if(isset($_POST['filename'])) { + $filename = filter_var($_POST['filename'], 'FILTER_SANITIZE_ENCODED'); + } else { + $filename = 'website.zip'; + } + } default : break; } diff --git a/include/text.php b/include/text.php index c8e0b1888..1729048e9 100644 --- a/include/text.php +++ b/include/text.php @@ -2267,11 +2267,11 @@ function design_tools() { } /** - * @brief Creates website import tools menu + * @brief Creates website portation tools menu * * @return string */ -function website_import_tools() { +function website_portation_tools() { $channel = App::get_channel(); $sys = false; @@ -2282,7 +2282,7 @@ function website_import_tools() { $sys = true; } - return replace_macros(get_markup_template('website_import_tools.tpl'), array( + return replace_macros(get_markup_template('website_portation_tools.tpl'), array( '$title' => t('Import'), '$import_label' => t('Import website...'), '$import_placeholder' => t('Select folder to import'), @@ -2290,29 +2290,11 @@ function website_import_tools() { '$file_import_text' => t('Import from cloud files:'), '$desc' => t('/cloud/channel/path/to/folder'), '$hint' => t('Enter path to website files'), - '$select' => t('Select folder'), - )); -} - -/** - * @brief Creates website export tools menu - * - * @return string - */ -function website_export_tools() { - - $channel = App::get_channel(); - $sys = false; - - if(App::$is_sys && is_site_admin()) { - require_once('include/channel.php'); - $channel = get_sys_channel(); - $sys = true; - } - - return replace_macros(get_markup_template('website_export_tools.tpl'), array( - '$title' => t('Export'), + '$select' => t('Select folder'), '$export_label' => t('Export website...'), + '$file_download_text' => t('Export to a zip file'), + '$filename_desc' => t('website.zip'), + '$filename_hint' => t('Enter a name for the zip file.'), )); } diff --git a/include/widgets.php b/include/widgets.php index fc2a9830b..2f8416f8e 100644 --- a/include/widgets.php +++ b/include/widgets.php @@ -779,7 +779,7 @@ function widget_design_tools($arr) { return design_tools(); } -function widget_website_import_tools($arr) { +function widget_website_portation_tools($arr) { // mod menu doesn't load a profile. For any modules which load a profile, check it. // otherwise local_channel() is sufficient for permissions. @@ -791,22 +791,7 @@ function widget_website_import_tools($arr) { if(! local_channel()) return ''; - return website_import_tools(); -} - -function widget_website_export_tools($arr) { - - // mod menu doesn't load a profile. For any modules which load a profile, check it. - // otherwise local_channel() is sufficient for permissions. - - if(App::$profile['profile_uid']) - if((App::$profile['profile_uid'] != local_channel()) && (! App::$is_sys)) - return ''; - - if(! local_channel()) - return ''; - - return website_export_tools(); + return website_portation_tools(); } function widget_findpeople($arr) { diff --git a/view/pdl/mod_webpages.pdl b/view/pdl/mod_webpages.pdl index 292d6791c..9e4d604ba 100644 --- a/view/pdl/mod_webpages.pdl +++ b/view/pdl/mod_webpages.pdl @@ -1,5 +1,4 @@ [region=aside] [widget=design_tools][/widget] -[widget=website_import_tools][/widget] -[widget=website_export_tools][/widget] +[widget=website_portation_tools][/widget] [/region] \ No newline at end of file diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl new file mode 100644 index 000000000..4ee14359d --- /dev/null +++ b/view/tpl/webpage_export_list.tpl @@ -0,0 +1,21 @@ +
+
+ +
+
+ +
+

{{$title}}

+
+
+
+
+ +
+
+ +
+
+
+
+ diff --git a/view/tpl/website_export_tools.tpl b/view/tpl/website_export_tools.tpl deleted file mode 100644 index 36f5c8e24..000000000 --- a/view/tpl/website_export_tools.tpl +++ /dev/null @@ -1,14 +0,0 @@ -
-

{{$title}}

- -
diff --git a/view/tpl/website_import_tools.tpl b/view/tpl/website_import_tools.tpl deleted file mode 100644 index cb3e6b524..000000000 --- a/view/tpl/website_import_tools.tpl +++ /dev/null @@ -1,37 +0,0 @@ -
-

{{$title}}

- -
diff --git a/view/tpl/website_portation_tools.tpl b/view/tpl/website_portation_tools.tpl new file mode 100644 index 000000000..d9ebed179 --- /dev/null +++ b/view/tpl/website_portation_tools.tpl @@ -0,0 +1,58 @@ +
+ + +
-- cgit v1.2.3 From 40a7d3821098c095a01e2a30b2b49c53bb38e2cf Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Fri, 19 Aug 2016 19:32:52 -0400 Subject: Pages populate the available-to-export list. New checkbox class for smaller boxes. --- Zotlabs/Module/Webpages.php | 14 +++++--- include/import.php | 62 +++++++++++++++++++++++++++++++++ view/css/mod_webpages.css | 74 ++++++++++++++++++++++++++++++++++++++++ view/tpl/webpage_export_list.tpl | 42 +++++++++++++++++++---- 4 files changed, 182 insertions(+), 10 deletions(-) diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 439d60a89..b927f95df 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -41,7 +41,6 @@ class Webpages extends \Zotlabs\Web\Controller { $uid = local_channel(); $owner = 0; - $channel = null; $observer = \App::get_observer(); $channel = \App::get_channel(); @@ -64,10 +63,19 @@ class Webpages extends \Zotlabs\Web\Controller { break; case 'export_select_list': $_SESSION['action'] = null; + if(!$uid) { + $_SESSION['export'] = null; + break; + } + require_once('include/import.php'); + + $elements = get_webpage_elements($channel, 'pages'); + logger(json_encode($elements), LOGGER_DEBUG); $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( '$title' => t('Export Webpage Elements'), '$exportbtn' => t('Export selected'), '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' + '$pages' => $elements['pages'], )); $_SESSION['export'] = null; return $o; @@ -243,7 +251,6 @@ class Webpages extends \Zotlabs\Web\Controller { } function post() { - logger(json_encode($_REQUEST), LOGGER_DEBUG); $action = $_REQUEST['action']; if( $action ){ switch ($action) { @@ -396,11 +403,10 @@ class Webpages extends \Zotlabs\Web\Controller { case 'exportzipfile': if(isset($_POST['w_download'])) { - logger($_POST['w_download'], LOGGER_DEBUG); $_SESSION['action'] = 'export_select_list'; $_SESSION['export'] = 'zipfile'; if(isset($_POST['filename'])) { - $filename = filter_var($_POST['filename'], 'FILTER_SANITIZE_ENCODED'); + $filename = filter_var($_POST['filename'], FILTER_SANITIZE_ENCODED); } else { $filename = 'website.zip'; } diff --git a/include/import.php b/include/import.php index 84881a420..f7df636f3 100644 --- a/include/import.php +++ b/include/import.php @@ -1472,3 +1472,65 @@ function scan_webpage_elements($path, $type, $cloud = false) { return $element; } + +function get_webpage_elements($channel, $type = 'all') { + $elements = array(); + if(!$channel['channel_id']) { + return null; + } + switch ($type) { + case 'pages': + $elements['pages'] = null; + $owner = $channel['channel_id']; + + $sql_extra = item_permissions_sql($owner); + + + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_WEBPAGE) + ); + + $pages = null; + + if($r) { + $elements['pages'] = array(); + $pages = array(); + foreach($r as $rr) { + unobscure($rr); + + //$lockstate = (($rr['allow_cid'] || $rr['allow_gid'] || $rr['deny_cid'] || $rr['deny_gid']) ? 'lock' : 'unlock'); + + $element_arr = array( + 'type' => 'webpage', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'pagetitle' => $rr['v'], + 'mid' => $rr['mid'], + 'layout_mid' => $rr['layout_mid'] + ); + $pages[$rr['iid']][] = array( + 'url' => $rr['iid'], + 'pagetitle' => $rr['v'], + 'title' => $rr['title'], + 'created' => datetime_convert('UTC',date_default_timezone_get(),$rr['created']), + 'edited' => datetime_convert('UTC',date_default_timezone_get(),$rr['edited']), + 'bb_element' => '[element]' . base64url_encode(json_encode($element_arr)) . '[/element]', + //'lockstate' => $lockstate + ); + $elements['pages'][] = $element_arr; + } + + } + break; + + default: + return null; + } + return $elements; +} \ No newline at end of file diff --git a/view/css/mod_webpages.css b/view/css/mod_webpages.css index f72f632dd..805d95dc2 100644 --- a/view/css/mod_webpages.css +++ b/view/css/mod_webpages.css @@ -119,3 +119,77 @@ opacity: 1; } + +/* SQUARED THREE */ +.squaredThree { + width: 14px; + height: 14px; + background: #fcfff4; + + background: -webkit-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -moz-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -o-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: -ms-linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + background: linear-gradient(top, #fcfff4 0%, #dfe5d7 40%, #b3bead 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fcfff4', endColorstr='#b3bead',GradientType=0 ); + margin: 5px auto; + + -webkit-box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + -moz-box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0,0,0,0.5); + position: relative; +} + +.squaredThree label { + cursor: pointer; + position: absolute; + width: 10px; + height: 10px; + left: 2px; + top: 2px; + + -webkit-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + -moz-box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + box-shadow: inset 0px 1px 1px rgba(0,0,0,0.5), 0px 1px 0px rgba(255,255,255,1); + + background: -webkit-linear-gradient(top, #222 0%, #45484d 100%); + background: -moz-linear-gradient(top, #222 0%, #45484d 100%); + background: -o-linear-gradient(top, #222 0%, #45484d 100%); + background: -ms-linear-gradient(top, #222 0%, #45484d 100%); + background: linear-gradient(top, #222 0%, #45484d 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#222', endColorstr='#45484d',GradientType=0 ); +} + +.squaredThree label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; + filter: alpha(opacity=0); + opacity: 0; + content: ''; + position: absolute; + width: 9px; + height: 5px; + background: transparent; + top: 2px; + left: 2px; + border: 3px solid #fcfff4; + border-top: none; + border-right: none; + + -webkit-transform: rotate(-45deg); + -moz-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + transform: rotate(-45deg); +} + +.squaredThree label:hover::after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)"; + filter: alpha(opacity=30); + opacity: 0.3; +} + +.squaredThree input[type=checkbox]:checked + label:after { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; +} \ No newline at end of file diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl index 4ee14359d..1b943ace8 100644 --- a/view/tpl/webpage_export_list.tpl +++ b/view/tpl/webpage_export_list.tpl @@ -9,13 +9,43 @@
-
- -
-
- +
+ +
+ +
+

Pages

+
+ + + {{foreach $pages as $page}} + + + + + + + {{/foreach}} +
Export?Page TitlePage LinkType
+
+ + +
+
+
+ {{$page.title}}
+
+
+
+ {{$page.pagetitle}}
+
+
+
+ {{$page.mimetype}}
+
+
+
-
-- cgit v1.2.3 From 80ed05d45d0e9f7f23848919a3a8af388988fdd9 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Sat, 20 Aug 2016 15:22:30 -0400 Subject: Export of pages and their layouts to a zip file works. Bug on importing the exported zip file for pages. --- Zotlabs/Lib/ExtendedZip.php | 57 ++++++++++++++ Zotlabs/Module/Webpages.php | 142 +++++++++++++++++++++++++++++++++-- include/import.php | 45 ++++++++++- view/tpl/webpage_export_list.tpl | 6 +- view/tpl/website_portation_tools.tpl | 2 +- 5 files changed, 242 insertions(+), 10 deletions(-) create mode 100644 Zotlabs/Lib/ExtendedZip.php diff --git a/Zotlabs/Lib/ExtendedZip.php b/Zotlabs/Lib/ExtendedZip.php new file mode 100644 index 000000000..a40110c55 --- /dev/null +++ b/Zotlabs/Lib/ExtendedZip.php @@ -0,0 +1,57 @@ +addEmptyDir($localname); + $this->_addTree($dirname, $localname); + } + + // Internal function, to recurse + protected function _addTree($dirname, $localname) { + $dir = opendir($dirname); + while ($filename = readdir($dir)) { + // Discard . and .. + if ($filename == '.' || $filename == '..') + continue; + + // Proceed according to type + $path = $dirname . '/' . $filename; + $localpath = $localname ? ($localname . '/' . $filename) : $filename; + if (is_dir($path)) { + // Directory: add & recurse + $this->addEmptyDir($localpath); + $this->_addTree($path, $localpath); + } + else if (is_file($path)) { + // File: just add + $this->addFile($path, $localpath); + } + } + closedir($dir); + } + + // Helper function + public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '') { + $zip = new self(); + $zip->open($zipFilename, $flags); + $zip->addTree($dirname, $localname); + $zip->close(); + } + +} diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index b927f95df..daa323007 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -70,7 +70,6 @@ class Webpages extends \Zotlabs\Web\Controller { require_once('include/import.php'); $elements = get_webpage_elements($channel, 'pages'); - logger(json_encode($elements), LOGGER_DEBUG); $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( '$title' => t('Export Webpage Elements'), '$exportbtn' => t('Export selected'), @@ -405,20 +404,151 @@ class Webpages extends \Zotlabs\Web\Controller { if(isset($_POST['w_download'])) { $_SESSION['action'] = 'export_select_list'; $_SESSION['export'] = 'zipfile'; - if(isset($_POST['filename'])) { - $filename = filter_var($_POST['filename'], FILTER_SANITIZE_ENCODED); + if(isset($_POST['zipfilename']) && $_POST['zipfilename'] !== '') { + $filename = filter_var($_POST['zipfilename'], FILTER_SANITIZE_ENCODED); } else { $filename = 'website.zip'; } + $_SESSION['zipfilename'] = $filename; + } + + break; + + case 'zipfile': + + $tmp_folder_name = random_string(10); + $zip_folder_name = random_string(10); + $zip_filename = $_SESSION['zipfilename']; + $tmp_folderpath = '/tmp/' . $tmp_folder_name; + $zip_folderpath = '/tmp/' . $zip_folder_name; + if (!mkdir($zip_folderpath, 0770, false)) { + logger('Error creating zip file export folder: ' . $zip_folderpath, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating zip file export folder')); + } + $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + + $checkedpages = $_POST['page']; + $pages = []; + if (!empty($checkedpages)) { + foreach ($checkedpages as $mid) { + + $channel = \App::get_channel(); + $p = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and item.mid = '%s' and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d", + intval($channel['channel_id']), + dbesc($mid), + intval(ITEM_TYPE_WEBPAGE) + ); + + if($p) { + foreach ($p as $pp) { + // Get the associated layout + $layoutinfo = array(); + if($pp['layout_mid']) { + // TODO: Should we check for ownership here if the layout is already associated with the page? + $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid + where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", + dbesc($pp['layout_mid']), + intval($channel['channel_id']) + ); + if($l) { + $l = $l[0]; + $layoutinfo = array( + 'body' => $l['body'], + 'mimetype' => $l['mimetype'], + 'description' => $l['title'], + 'name' => $l['v'], + 'json' => array( + 'description' => $l['title'], + 'name' => $l['v'], + ) + ); + switch ($layoutinfo['mimetype']) { + case 'text/bbcode': + default: + $layout_ext = 'bbcode'; + break; + } + $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; + $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; + $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; + $layoutinfo['json']['contentfile'] = $layout_filename; + $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; + if (!mkdir($tmp_layoutfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($layout_filepath, $layoutinfo['body']); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + switch ($pp['mimetype']) { + case 'text/html': + $page_ext = 'html'; + break; + case 'text/bbcode': + $page_ext = 'bbcode'; + break; + case 'text/markdown': + $page_ext = 'md'; + break; + case 'application/x-pdl': + $page_ext = 'pdl'; + break; + case 'application/x-php': + $page_ext = 'php'; + break; + default: + break; + } + $pageinfo = array( + 'title' => $pp['title'], + 'body' => $pp['body'], + 'pagelink' => $pp['v'], + 'mimetype' => $pp['mimetype'], + 'contentfile' => $pp['v'] . '.' . $page_ext, + 'layout' => ((x($layoutinfo,'name')) ? $layoutinfo['name'] : ''), + 'json' => array( + 'title' => $pp['title'], + 'pagelink' => $pp['v'], + 'mimetype' => $pp['mimetype'], + 'layout' => ((x($layoutinfo,'name')) ? $layoutinfo['name'] : ''), + ) + ); + $page_filename = $pageinfo['pagelink'] . '.' . $page_ext; + $tmp_pagefolder = $tmp_folderpath . '/pages/' . $pageinfo['pagelink']; + $page_filepath = $tmp_pagefolder . '/' . $page_filename; + $page_jsonpath = $tmp_pagefolder . '/page.json'; + $pageinfo['json']['contentfile'] = $page_filename; + if (!mkdir($tmp_pagefolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_pagefolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($page_filepath, $pageinfo['body']); + file_put_contents($page_jsonpath, json_encode($pageinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + } + // TODO: Generate zipped file and return for download + + //create_zip_file($tmp_folderpath, $zip_filepath, true); + + \Zotlabs\Lib\ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, \ZipArchive::CREATE); + + header('Content-disposition: attachment; filename="' . $zip_filename . '"'); + header("Content-Type: application/zip"); + readfile($zip_filepath); + rrmdir($zip_folderpath); + rrmdir($tmp_folderpath); + break; default : break; } } - - - } } diff --git a/include/import.php b/include/import.php index f7df636f3..b4d342273 100644 --- a/include/import.php +++ b/include/import.php @@ -1533,4 +1533,47 @@ function get_webpage_elements($channel, $type = 'all') { return null; } return $elements; -} \ No newline at end of file +} + +/* creates a compressed zip file */ + +function create_zip_file($files = array(), $destination = '', $overwrite = false) { + //if the zip file already exists and overwrite is false, return false + if (file_exists($destination) && !$overwrite) { + return false; + } + //vars + $valid_files = array(); + //if files were passed in... + if (is_array($files)) { + //cycle through each file + foreach ($files as $file) { + //make sure the file exists + if (file_exists($file)) { + $valid_files[] = $file; + } + } + } + + //if we have good files... + if (count($valid_files)) { + //create the archive + $zip = new ZipArchive(); + if ($zip->open($destination, $overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) { + return false; + } + //add the files + foreach ($valid_files as $file) { + $zip->addFile($file, $file); + } + //debug + //echo 'The zip archive contains ',$zip->numFiles,' files with a status of ',$zip->status; + //close the zip -- done! + $zip->close(); + + //check to make sure the file exists + return file_exists($destination); + } else { + return false; + } +} diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl index 1b943ace8..d980ea981 100644 --- a/view/tpl/webpage_export_list.tpl +++ b/view/tpl/webpage_export_list.tpl @@ -17,7 +17,9 @@

Pages

- + + + {{foreach $pages as $page}} diff --git a/view/tpl/website_portation_tools.tpl b/view/tpl/website_portation_tools.tpl index d9ebed179..2f68a7393 100644 --- a/view/tpl/website_portation_tools.tpl +++ b/view/tpl/website_portation_tools.tpl @@ -46,7 +46,7 @@
- +
-- cgit v1.2.3 From 2d42d587388aeaedf8b53b2b56b3b4ccda37af03 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Sat, 20 Aug 2016 16:05:27 -0400 Subject: Layouts list and are selectable for export to the zip file --- Zotlabs/Module/Webpages.php | 70 ++++++++++++++++++++++++++++++++-------- include/import.php | 48 +++++++++++++++++++++++++-- view/tpl/webpage_export_list.tpl | 35 ++++++++++++++++++++ 3 files changed, 137 insertions(+), 16 deletions(-) diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index daa323007..9a0e311d6 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -69,12 +69,14 @@ class Webpages extends \Zotlabs\Web\Controller { } require_once('include/import.php'); - $elements = get_webpage_elements($channel, 'pages'); + $pages = get_webpage_elements($channel, 'pages'); + $layouts = get_webpage_elements($channel, 'layouts'); $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( '$title' => t('Export Webpage Elements'), '$exportbtn' => t('Export selected'), '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' - '$pages' => $elements['pages'], + '$pages' => $pages['pages'], + '$layouts' => $layouts['layouts'], )); $_SESSION['export'] = null; return $o; @@ -417,6 +419,8 @@ class Webpages extends \Zotlabs\Web\Controller { case 'zipfile': + $channel = \App::get_channel(); + $tmp_folder_name = random_string(10); $zip_folder_name = random_string(10); $zip_filename = $_SESSION['zipfilename']; @@ -428,12 +432,54 @@ class Webpages extends \Zotlabs\Web\Controller { } $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + $checkedlayouts = $_POST['layout']; + $layouts = []; + if (!empty($checkedlayouts)) { + foreach ($checkedlayouts as $mid) { + $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid + where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", + dbesc($mid), + intval($channel['channel_id']) + ); + if($l) { + $l = $l[0]; + $layoutinfo = array( + 'body' => $l['body'], + 'mimetype' => $l['mimetype'], + 'description' => $l['title'], + 'name' => $l['v'], + 'json' => array( + 'description' => $l['title'], + 'name' => $l['v'], + ) + ); + switch ($layoutinfo['mimetype']) { + case 'text/bbcode': + default: + $layout_ext = 'bbcode'; + break; + } + $layout_filename = $layoutinfo['name'] . '.' . $layout_ext; + $tmp_layoutfolder = $tmp_folderpath . '/layouts/' . $layoutinfo['name']; + $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; + $layoutinfo['json']['contentfile'] = $layout_filename; + $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($layout_filepath, $layoutinfo['body']); + file_put_contents($layout_jsonpath, json_encode($layoutinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + $checkedpages = $_POST['page']; $pages = []; if (!empty($checkedpages)) { foreach ($checkedpages as $mid) { - - $channel = \App::get_channel(); + $p = q("select * from iconfig left join item on iconfig.iid = item.id where item.uid = %d and item.mid = '%s' and iconfig.cat = 'system' and iconfig.k = 'WEBPAGE' and item_type = %d", intval($channel['channel_id']), @@ -446,7 +492,6 @@ class Webpages extends \Zotlabs\Web\Controller { // Get the associated layout $layoutinfo = array(); if($pp['layout_mid']) { - // TODO: Should we check for ownership here if the layout is already associated with the page? $l = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig left join item on item.id = iconfig.iid where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' order by iconfig.v asc limit 1", @@ -476,7 +521,7 @@ class Webpages extends \Zotlabs\Web\Controller { $layout_filepath = $tmp_layoutfolder . '/' . $layout_filename; $layoutinfo['json']['contentfile'] = $layout_filename; $layout_jsonpath = $tmp_layoutfolder . '/layout.json'; - if (!mkdir($tmp_layoutfolder, 0770, true)) { + if (!is_dir($tmp_layoutfolder) && !mkdir($tmp_layoutfolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_layoutfolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } @@ -522,7 +567,7 @@ class Webpages extends \Zotlabs\Web\Controller { $page_filepath = $tmp_pagefolder . '/' . $page_filename; $page_jsonpath = $tmp_pagefolder . '/page.json'; $pageinfo['json']['contentfile'] = $page_filename; - if (!mkdir($tmp_pagefolder, 0770, true)) { + if (!is_dir($tmp_pagefolder) && !mkdir($tmp_pagefolder, 0770, true)) { logger('Error creating temp export folder: ' . $tmp_pagefolder, LOGGER_NORMAL); json_return_and_die(array('message' => 'Error creating temp export folder')); } @@ -532,17 +577,14 @@ class Webpages extends \Zotlabs\Web\Controller { } } } - // TODO: Generate zipped file and return for download - - //create_zip_file($tmp_folderpath, $zip_filepath, true); - + // Generate the zip file \Zotlabs\Lib\ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, \ZipArchive::CREATE); - + // Output the file for download header('Content-disposition: attachment; filename="' . $zip_filename . '"'); header("Content-Type: application/zip"); readfile($zip_filepath); - rrmdir($zip_folderpath); - rrmdir($tmp_folderpath); + rrmdir($zip_folderpath); // delete temporary files + rrmdir($tmp_folderpath); // delete temporary files break; default : break; diff --git a/include/import.php b/include/import.php index b4d342273..a62747d9b 100644 --- a/include/import.php +++ b/include/import.php @@ -1479,6 +1479,8 @@ function get_webpage_elements($channel, $type = 'all') { return null; } switch ($type) { + case 'all': + // If all, execute all the pages, layouts, blocks case statements case 'pages': $elements['pages'] = null; $owner = $channel['channel_id']; @@ -1527,10 +1529,52 @@ function get_webpage_elements($channel, $type = 'all') { } } - break; + if($type !== 'all') { + break; + } + + case 'layouts': + $elements['layouts'] = null; + $owner = $channel['channel_id']; + + $sql_extra = item_permissions_sql($owner); + + + $r = q("select * from iconfig left join item on iconfig.iid = item.id + where item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'PDL' and item_type = %d + $sql_extra order by item.created desc", + intval($owner), + intval(ITEM_TYPE_PDL) + ); + + $layouts = null; + + if($r) { + $elements['layouts'] = array(); + $layouts = array(); + foreach($r as $rr) { + unobscure($rr); + $elements['layouts'][] = array( + 'type' => 'layout', + 'description' => $rr['title'], // description of the layout + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], // name of reference for the layout + 'mid' => $rr['mid'], + ); + } + + } + + if($type !== 'all') { + break; + } + default: - return null; + break; } return $elements; } diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl index d980ea981..faffbf8c6 100644 --- a/view/tpl/webpage_export_list.tpl +++ b/view/tpl/webpage_export_list.tpl @@ -47,6 +47,41 @@ {{/foreach}}
Export?Page TitlePage LinkType
Export?Page TitlePage LinkType
@@ -27,7 +29,7 @@ -
+
{{$page.title}}
+ +
+

Layouts

+
+ + + + + {{foreach $layouts as $layout}} + + + + + + + {{/foreach}} +
Export?Layout NameLayout DescriptionType
+
+ + +
+
+
+ {{$layout.name}}
+
+
+
+ {{$layout.description}}
+
+
+
+ {{$layout.mimetype}}
+
+
+
-- cgit v1.2.3 From 1c61e316b477460f05b263b9a01cc5d37b5e8cb6 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Sat, 20 Aug 2016 21:08:15 -0400 Subject: Block export and re-import works. Fixed bug where layout content was not being imported properly. --- Zotlabs/Module/Webpages.php | 69 +++++++++++++++++++++++++++++++++++++++- include/import.php | 55 ++++++++++++++++++++++++++++++-- view/tpl/webpage_export_list.tpl | 36 +++++++++++++++++++++ 3 files changed, 157 insertions(+), 3 deletions(-) diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 9a0e311d6..0fec06c38 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -71,12 +71,14 @@ class Webpages extends \Zotlabs\Web\Controller { $pages = get_webpage_elements($channel, 'pages'); $layouts = get_webpage_elements($channel, 'layouts'); + $blocks = get_webpage_elements($channel, 'blocks'); $o .= replace_macros(get_markup_template('webpage_export_list.tpl'), array( '$title' => t('Export Webpage Elements'), '$exportbtn' => t('Export selected'), '$action' => $_SESSION['export'], // value should be 'zipfile' or 'cloud' '$pages' => $pages['pages'], '$layouts' => $layouts['layouts'], + '$blocks' => $blocks['blocks'], )); $_SESSION['export'] = null; return $o; @@ -314,6 +316,7 @@ class Webpages extends \Zotlabs\Web\Controller { $path = $website; } $elements['pages'] = scan_webpage_elements($path, 'page', $cloud); + logger('$elements pages: ' . json_encode($elements['pages'])); $elements['layouts'] = scan_webpage_elements($path, 'layout', $cloud); $elements['blocks'] = scan_webpage_elements($path, 'block', $cloud); $_SESSION['blocks'] = $elements['blocks']; @@ -331,7 +334,8 @@ class Webpages extends \Zotlabs\Web\Controller { // If the website elements were imported from a zip file, delete the temporary decompressed files if ($cloud === false && $website && $elements) { - rrmdir($website); // Delete the temporary decompressed files + $_SESSION['tempimportpath'] = $website; + //rrmdir($website); // Delete the temporary decompressed files } break; @@ -399,6 +403,10 @@ class Webpages extends \Zotlabs\Web\Controller { if(!(empty($_SESSION['import_pages']) && empty($_SESSION['import_blocks']) && empty($_SESSION['import_layouts']))) { info( t('Import complete.') . EOL); } + if(isset($_SESSION['tempimportpath'])) { + rrmdir($_SESSION['tempimportpath']); // Delete the temporary decompressed files + unset($_SESSION['tempimportpath']); + } break; case 'exportzipfile': @@ -432,6 +440,64 @@ class Webpages extends \Zotlabs\Web\Controller { } $zip_filepath = '/tmp/' . $zip_folder_name . '/' . $zip_filename; + $checkedblocks = $_POST['block']; + $blocks = []; + if (!empty($checkedblocks)) { + foreach ($checkedblocks as $mid) { + $b = q("select iconfig.v, iconfig.k, mimetype, title, body from iconfig + left join item on item.id = iconfig.iid + where mid = '%s' and item.uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' order by iconfig.v asc limit 1", + dbesc($mid), + intval($channel['channel_id']) + ); + if($b) { + $b = $b[0]; + $blockinfo = array( + 'body' => $b['body'], + 'mimetype' => $b['mimetype'], + 'title' => $b['title'], + 'name' => $b['v'], + 'json' => array( + 'title' => $b['title'], + 'name' => $b['v'], + 'mimetype' => $b['mimetype'], + ) + ); + switch ($blockinfo['mimetype']) { + case 'text/html': + $block_ext = 'html'; + break; + case 'text/bbcode': + $block_ext = 'bbcode'; + break; + case 'text/markdown': + $block_ext = 'md'; + break; + case 'application/x-pdl': + $block_ext = 'pdl'; + break; + case 'application/x-php': + $block_ext = 'php'; + break; + default: + $block_ext = 'bbcode'; + break; + } + $block_filename = $blockinfo['name'] . '.' . $block_ext; + $tmp_blockfolder = $tmp_folderpath . '/blocks/' . $blockinfo['name']; + $block_filepath = $tmp_blockfolder . '/' . $block_filename; + $blockinfo['json']['contentfile'] = $block_filename; + $block_jsonpath = $tmp_blockfolder . '/block.json'; + if (!is_dir($tmp_blockfolder) && !mkdir($tmp_blockfolder, 0770, true)) { + logger('Error creating temp export folder: ' . $tmp_blockfolder, LOGGER_NORMAL); + json_return_and_die(array('message' => 'Error creating temp export folder')); + } + file_put_contents($block_filepath, $blockinfo['body']); + file_put_contents($block_jsonpath, json_encode($blockinfo['json'], JSON_UNESCAPED_SLASHES)); + } + } + } + $checkedlayouts = $_POST['layout']; $layouts = []; if (!empty($checkedlayouts)) { @@ -452,6 +518,7 @@ class Webpages extends \Zotlabs\Web\Controller { 'json' => array( 'description' => $l['title'], 'name' => $l['v'], + 'mimetype' => $l['mimetype'], ) ); switch ($layoutinfo['mimetype']) { diff --git a/include/import.php b/include/import.php index a62747d9b..10664aa53 100644 --- a/include/import.php +++ b/include/import.php @@ -1311,9 +1311,15 @@ function scan_webpage_elements($path, $type, $cloud = false) { return false; } $content = file_get_contents($folder . '/' . $contentfilename); + logger('contentfile: ' . $folder . '/' . $contentfilename, LOGGER_DEBUG); + logger('content: ' . $content, LOGGER_DEBUG); if (!$content) { - logger('Failed to get file content for ' . $metadata['contentfile']); - return false; + if(is_readable($folder . '/' . $contentfilename)) { + $content = ''; + } else { + logger('Failed to get file content for ' . $metadata['contentfile']); + return false; + } } $elements[] = $metadata; } @@ -1403,6 +1409,10 @@ function scan_webpage_elements($path, $type, $cloud = false) { } // Import the actual element content $arr['body'] = file_get_contents($element['path']); + if($arr['item_type'] === ITEM_TYPE_PDL) { + logger(' body: ' . $arr['body'], LOGGER_DEBUG); + logger(' path: ' . $element['path'], LOGGER_DEBUG); + } // The element owner is the channel importing the elements $arr['owner_xchan'] = get_observer_hash(); // The author is either the owner or whomever was specified @@ -1573,6 +1583,47 @@ function get_webpage_elements($channel, $type = 'all') { break; } + case 'blocks': + $elements['blocks'] = null; + $owner = $channel['channel_id']; + + $sql_extra = item_permissions_sql($owner); + + + $r = q("select iconfig.iid, iconfig.k, iconfig.v, mid, title, body, mimetype, created, edited from iconfig + left join item on iconfig.iid = item.id + where uid = %d and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK' + and item_type = %d order by item.created desc", + intval($owner), + intval(ITEM_TYPE_BLOCK) + ); + + $blocks = null; + + if($r) { + $elements['blocks'] = array(); + $blocks = array(); + foreach($r as $rr) { + unobscure($rr); + + $elements['blocks'][] = array( + 'type' => 'block', + 'title' => $rr['title'], + 'body' => $rr['body'], + 'created' => $rr['created'], + 'edited' => $rr['edited'], + 'mimetype' => $rr['mimetype'], + 'name' => $rr['v'], + 'mid' => $rr['mid'] + ); + } + + } + + if($type !== 'all') { + break; + } + default: break; } diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl index faffbf8c6..1df4586b0 100644 --- a/view/tpl/webpage_export_list.tpl +++ b/view/tpl/webpage_export_list.tpl @@ -82,6 +82,42 @@ {{/foreach}} + +
+

Blocks

+
+ + + + + {{foreach $blocks as $block}} + + + + + + + {{/foreach}} +
Export?Block TitleBlock NameType
+
+ + +
+
+
+ {{$block.title}}
+
+
+
+ {{$block.name}}
+
+
+
+ {{$block.mimetype}}
+
+
+
+ -- cgit v1.2.3 From d39cf23b2f15ed94048ea7596d7581be7ef8c001 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Sun, 21 Aug 2016 06:53:19 -0400 Subject: Visual improvements to the export element table --- view/tpl/webpage_export_list.tpl | 50 ++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl index 1df4586b0..1d28f62df 100644 --- a/view/tpl/webpage_export_list.tpl +++ b/view/tpl/webpage_export_list.tpl @@ -12,33 +12,33 @@
-
-

Pages

+
+
- + {{foreach $pages as $page}} - - - -
Export?Page TitlePage LinkType
Page LinkPage TitleType
+
+
- {{$page.title}}
+ {{$page.pagetitle}}
+
- {{$page.pagetitle}}
+ {{$page.title}}
+
{{$page.mimetype}}
@@ -47,33 +47,33 @@ {{/foreach}}
- +
-

Layouts

+
- + {{foreach $layouts as $layout}} - - - -
Export?Layout NameLayout DescriptionType
Layout NameLayout DescriptionType
+
+
{{$layout.name}}
+
{{$layout.description}}
+
{{$layout.mimetype}}
@@ -82,33 +82,33 @@ {{/foreach}}
- +
-

Blocks

+
- + {{foreach $blocks as $block}} - - - -
Export?Block TitleBlock NameType
Block NameBlock TitleType
+
+
- {{$block.title}}
+ {{$block.name}}
+
- {{$block.name}}
+ {{$block.title}}
+
{{$block.mimetype}}
-- cgit v1.2.3 From 305e0538d293ff4ad72f30b11998ae3b06efa4ea Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Tue, 23 Aug 2016 06:55:26 -0400 Subject: Website export to cloud files works. Created new recursive copy function in attach.php. --- Zotlabs/Module/Webpages.php | 55 ++++++++++++++++++++++++++++++------ include/attach.php | 47 ++++++++++++++++++++++++++++++ include/text.php | 4 +++ view/tpl/website_portation_tools.tpl | 16 ++++++++++- 4 files changed, 113 insertions(+), 9 deletions(-) diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 0fec06c38..028cc5114 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -424,7 +424,18 @@ class Webpages extends \Zotlabs\Web\Controller { } break; + + case 'exportcloud': + logger('exportcloud', LOGGER_DEBUG); + if(isset($_POST['exportcloudpath']) && $_POST['exportcloudpath'] !== '') { + $_SESSION['action'] = 'export_select_list'; + $_SESSION['export'] = 'cloud'; + $_SESSION['exportcloudpath'] = filter_var($_POST['exportcloudpath'], FILTER_SANITIZE_ENCODED); + } + break; + + case 'cloud': case 'zipfile': $channel = \App::get_channel(); @@ -644,18 +655,46 @@ class Webpages extends \Zotlabs\Web\Controller { } } } - // Generate the zip file - \Zotlabs\Lib\ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, \ZipArchive::CREATE); - // Output the file for download - header('Content-disposition: attachment; filename="' . $zip_filename . '"'); - header("Content-Type: application/zip"); - readfile($zip_filepath); - rrmdir($zip_folderpath); // delete temporary files - rrmdir($tmp_folderpath); // delete temporary files + if($action === 'zipfile') { + // Generate the zip file + \Zotlabs\Lib\ExtendedZip::zipTree($tmp_folderpath, $zip_filepath, \ZipArchive::CREATE); + // Output the file for download + header('Content-disposition: attachment; filename="' . $zip_filename . '"'); + header("Content-Type: application/zip"); + $success = readfile($zip_filepath); + } elseif ($action === 'cloud') { // Only zipfile or cloud should be possible values for $action here + if(isset($_SESSION['exportcloudpath'])) { + require_once('include/attach.php'); + $cloudpath = urldecode($_SESSION['exportcloudpath']); + $channel = \App::get_channel(); + $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); + if(!$dirpath) { + $x = attach_mkdirp($channel, $channel['channel_hash'], array('pathname' => $cloudpath)); + $folder_hash = (($x['success']) ? $x['data']['hash'] : ''); + + if (!$x['success']) { + logger('Failed to create cloud file folder', LOGGER_NORMAL); + } + $dirpath = get_dirpath_by_cloudpath($channel, $cloudpath); + if (!is_dir($dirpath)) { + logger('Failed to create cloud file folder', LOGGER_NORMAL); + } + } + + $success = copy_folder_to_cloudfiles($channel, $channel['channel_hash'], $tmp_folderpath, $cloudpath); + } + } + if(!$success) { + logger('Error exporting webpage elements', LOGGER_NORMAL); + } + + rrmdir($zip_folderpath); rrmdir($tmp_folderpath); // delete temporary files + break; default : break; } + } } diff --git a/include/attach.php b/include/attach.php index 172840b96..6b17cdca1 100644 --- a/include/attach.php +++ b/include/attach.php @@ -1994,3 +1994,50 @@ function get_filename_by_cloudname($cloudname, $channel, $storepath) { } return null; } + + +// recursively copy a directory into cloud files +function copy_folder_to_cloudfiles($channel, $observer_hash, $srcpath, $cloudpath) +{ + if (!is_dir($srcpath) || !is_readable($srcpath)) { + logger('Error reading source path: ' . $srcpath, LOGGER_NORMAL); + return false; + } + $nodes = array_diff(scandir($srcpath), array('.', '..')); + foreach ($nodes as $node) { + $clouddir = $cloudpath . '/' . $node; // Sub-folder in cloud files destination + $nodepath = $srcpath . '/' . $node; // Sub-folder in source path + if(is_dir($nodepath)) { + $x = attach_mkdirp($channel, $observer_hash, array('pathname' => $clouddir)); + if(!$x['success']) { + logger('Error creating cloud path: ' . $clouddir, LOGGER_NORMAL); + return false; + } + // Recursively call this function where the source and destination are the subfolders + $success = copy_folder_to_cloudfiles($channel, $observer_hash, $nodepath, $clouddir); + if(!$success) { + logger('Error copying contents of folder: ' . $nodepath, LOGGER_NORMAL); + return false; + } + } elseif (is_file($nodepath) && is_readable($nodepath)) { + $x = attach_store($channel, $observer_hash, 'import', + array( + 'directory' => $cloudpath, + 'src' => $nodepath, + 'filename' => $node, + 'filesize' => @filesize($nodepath), + 'preserve_original' => true) + ); + if(!$x['success']) { + logger('Error copying file: ' . $nodepath , LOGGER_NORMAL); + logger('Return value: ' . json_encode($x), LOGGER_NORMAL); + return false; + } + } else { + logger('Error scanning source path', LOGGER_NORMAL); + return false; + } + } + + return true; +} \ No newline at end of file diff --git a/include/text.php b/include/text.php index 1729048e9..99ac59ca7 100644 --- a/include/text.php +++ b/include/text.php @@ -2295,6 +2295,10 @@ function website_portation_tools() { '$file_download_text' => t('Export to a zip file'), '$filename_desc' => t('website.zip'), '$filename_hint' => t('Enter a name for the zip file.'), + '$cloud_export_text' => t('Export to cloud files'), + '$cloud_export_desc' => t('/path/to/export/folder'), + '$cloud_export_hint' => t('Enter a path to a cloud files destination.'), + '$cloud_export_select' => t('Specify folder'), )); } diff --git a/view/tpl/website_portation_tools.tpl b/view/tpl/website_portation_tools.tpl index 2f68a7393..10468b64e 100644 --- a/view/tpl/website_portation_tools.tpl +++ b/view/tpl/website_portation_tools.tpl @@ -35,7 +35,7 @@ -- cgit v1.2.3 From 52b80711f64c6108f11a2a49076a09564a5ae0ff Mon Sep 17 00:00:00 2001 From: redmatrix Date: Mon, 29 Aug 2016 21:21:09 -0700 Subject: remove duplicated include --- Zotlabs/Module/Apps.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Zotlabs/Module/Apps.php b/Zotlabs/Module/Apps.php index 4bdec4573..4dab621b2 100644 --- a/Zotlabs/Module/Apps.php +++ b/Zotlabs/Module/Apps.php @@ -1,7 +1,6 @@ Date: Tue, 30 Aug 2016 17:23:14 -0700 Subject: increase timeout on oembed remote fetches. This is a balancing act. Too short and many sites won't show up. Too long and you could be watching a spinner anxiously waiting for the page to load even if your own site is blindingly fast. --- include/oembed.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/include/oembed.php b/include/oembed.php index fe6f10d71..085637a00 100755 --- a/include/oembed.php +++ b/include/oembed.php @@ -156,9 +156,12 @@ function oembed_fetch_url($embedurl){ if ($action !== 'block') { // try oembed autodiscovery $redirects = 0; - $result = z_fetch_url($furl, false, $redirects, array('timeout' => 15, 'accept_content' => "text/*", 'novalidate' => true )); + $result = z_fetch_url($furl, false, $redirects, array('timeout' => 30, 'accept_content' => "text/*", 'novalidate' => true )); + if($result['success']) $html_text = $result['body']; + else + logger('fetch failure: ' . $furl); if($html_text) { $dom = @DOMDocument::loadHTML($html_text); @@ -171,7 +174,10 @@ function oembed_fetch_url($embedurl){ foreach($entries as $e){ $href = $e->getAttributeNode("href")->nodeValue; $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - $txt = $x['body']; + if($x['success']) + $txt = $x['body']; + else + logger('fetch failed: ' . $href); break; } // soundcloud is now using text/json+oembed instead of application/json+oembed, @@ -180,7 +186,10 @@ function oembed_fetch_url($embedurl){ foreach($entries as $e){ $href = $e->getAttributeNode("href")->nodeValue; $x = z_fetch_url($href . '&maxwidth=' . App::$videowidth); - $txt = $x['body']; + if($x['success']) + $txt = $x['body']; + else + logger('json fetch failed: ' . $href); break; } } -- cgit v1.2.3 From 9bd8384a571fb691a77bbe42c3def32ada1b7cb5 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 30 Aug 2016 18:40:37 -0700 Subject: first cut at project governance doc --- doc/general.bb | 2 ++ doc/project/governance.bb | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 doc/project/governance.bb diff --git a/doc/general.bb b/doc/general.bb index bbc85c900..89451beaf 100644 --- a/doc/general.bb +++ b/doc/general.bb @@ -1,6 +1,8 @@ [h2]Project and site information[/h2] [h3]$Projectname[/h3] [zrl=[baseurl]/help/Privacy]Privacy Policy[/zrl] +[zrl=[baseurl]/help/project/governance]Project Governance[/zrl] +[zrl=[baseurl]/help/contributor/convenant]Project Covenant and Code of Conduct[/zrl] [zrl=[baseurl]/help/history]$Projectname history[/zrl] [h3]External resources[/h3] [zrl=[baseurl]/help/external-resource-links]List of external resources[/zrl] diff --git a/doc/project/governance.bb b/doc/project/governance.bb new file mode 100644 index 000000000..e13f6218c --- /dev/null +++ b/doc/project/governance.bb @@ -0,0 +1,45 @@ +[h2]$Projectname Governance[/h2] + +Governance relates to the management of a project and particularly how this relates to conflict resolution. + +This project uses a dual-governance model. + +The project as a whole and the repository were created initially by Mike Macgirvin; who controls the project copyright, and the project license, and manages the project as a Self Appointed Benevolent Dictator for Life. He holds veto power over any project proposal or decision and his word is final. + +That said, Mike has no interest in running the day to day activities of the project and influencing its direction, other than to protect his own work from sabotage. + +The internal project structure contains multiple "configurations" known as 'basic', 'standard', and 'pro'. Mike's veto power extends to any proposal or decision which he feels might adversely affect the 'pro' configuration. + +The 'basic and 'standard' configurations are controlled completely by the community. If the proposal or decision is crafted in such a way that its effects are limited to these configurations, Mike will consider relinquishing his power of veto and convert it to a normal community vote. + +Mario Vavti has done an incredible amount of work on the usability and theming of the project and holds veto power over any proposal or decision which might impact usability and "look and feel"; and his decision is also final. + +Mario's veto power is likewise restricted to anything using the standard project 'theme'. If a new theme is created and an otherwise vetoed decision is implemented entirely in this different theme and has no impact on the standard project theme, his veto [b]may[/b] also be turned into a normal community vote. + +This ability to work around a veto is at the discretion of Mike and Mario. They [b]may[/b] choose to relinquish their veto if the scope of the work is limited as described above, and in most circumstances they will leave the decision to the community. They are not obligated to do so. + +[h3]Community Governance[/h3] + +Beyond those two special cases, the project is maintained and decisions made by the 'community'. The governance structure is still evolving. Until the structure is finalised, decisions are made in the following order: + +[ol] +[*] Lazy Consensus + +If a project proposal is made to one of the community governance forums and there are no serious objections in a "reasonable" amount of time from date of proposal (we usually provide 2-3 days for all interested parties to weigh in), no vote needs to be taken and the proposal will be considered approved. Some concerns may be raised at this time, but if these are addressed during discussion and work-arounds provided, it will still be considered approved. + +[*] Veto + +If a proposal is vetoed, it is not necessarily the final word. See above on how to convert a veto into a normal community vote. This can be done by framing the proposal so that it does not impact the 'pro' configuration or the standard theme. + +[*] Community Vote + +A decision which does not have a clear mandate or clear consensus, but is not vetoed, can be taken to a community vote. At present this is a simple popular vote in one of the applicable community forums. At this time, popular vote decides the outcome. This may change in the future if the community adopts a 'council' governance model. This document will be updated at that time with the updated governance rules. +[/ol] + +Community Voting does not always provide a pleasant outcome and can generate polarised factions in the community (hence the reason why other models are under consideration). If the proposal is 'down voted' there are still several things which can be done and the proposal re-submitted with slightly different parameters (convert to an addon, convert to an optional feature which is disabled by default, etc.). If interest in the feature is high and the vote is "close", it can generate lots of bad feelings amongst the losing voters. On such close votes, it is [b]strongly recommended[/b] that the proposer take steps to address any concerns that were raised and re-submit. + + + + + + -- cgit v1.2.3 From 16b7df37170483aa8d3dba9080a5f3f3940ec202 Mon Sep 17 00:00:00 2001 From: Andrew Manning Date: Tue, 30 Aug 2016 22:11:14 -0400 Subject: Imported webpage elements update existing elements properly. AllowCode permission is checked correctly and issues error on import if denied. --- Zotlabs/Module/Webpages.php | 2 -- include/import.php | 23 +++++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php index 028cc5114..acaca16c3 100644 --- a/Zotlabs/Module/Webpages.php +++ b/Zotlabs/Module/Webpages.php @@ -316,7 +316,6 @@ class Webpages extends \Zotlabs\Web\Controller { $path = $website; } $elements['pages'] = scan_webpage_elements($path, 'page', $cloud); - logger('$elements pages: ' . json_encode($elements['pages'])); $elements['layouts'] = scan_webpage_elements($path, 'layout', $cloud); $elements['blocks'] = scan_webpage_elements($path, 'block', $cloud); $_SESSION['blocks'] = $elements['blocks']; @@ -426,7 +425,6 @@ class Webpages extends \Zotlabs\Web\Controller { break; case 'exportcloud': - logger('exportcloud', LOGGER_DEBUG); if(isset($_POST['exportcloudpath']) && $_POST['exportcloudpath'] !== '') { $_SESSION['action'] = 'export_select_list'; $_SESSION['export'] = 'cloud'; diff --git a/include/import.php b/include/import.php index 10664aa53..ddffb2ac3 100644 --- a/include/import.php +++ b/include/import.php @@ -1311,8 +1311,6 @@ function scan_webpage_elements($path, $type, $cloud = false) { return false; } $content = file_get_contents($folder . '/' . $contentfilename); - logger('contentfile: ' . $folder . '/' . $contentfilename, LOGGER_DEBUG); - logger('content: ' . $content, LOGGER_DEBUG); if (!$content) { if(is_readable($folder . '/' . $contentfilename)) { $content = ''; @@ -1401,18 +1399,14 @@ function scan_webpage_elements($path, $type, $cloud = false) { ); $arr['mid'] = $arr['parent_mid'] = $iteminfo[0]['mid']; $arr['created'] = $iteminfo[0]['created']; - $arr['edited'] = (($element['edited']) ? datetime_convert('UTC', 'UTC', $element['edited']) : datetime_convert()); } else { // otherwise, generate the creation times and unique id - $arr['created'] = (($element['created']) ? datetime_convert('UTC', 'UTC', $element['created']) : datetime_convert()); - $arr['edited'] = datetime_convert('UTC', 'UTC', '0000-00-00 00:00:00'); + $arr['created'] = datetime_convert('UTC', 'UTC'); $arr['mid'] = $arr['parent_mid'] = item_message_id(); } + // Update the edited time whether or not the element already exists + $arr['edited'] = datetime_convert('UTC', 'UTC'); // Import the actual element content $arr['body'] = file_get_contents($element['path']); - if($arr['item_type'] === ITEM_TYPE_PDL) { - logger(' body: ' . $arr['body'], LOGGER_DEBUG); - logger(' path: ' . $element['path'], LOGGER_DEBUG); - } // The element owner is the channel importing the elements $arr['owner_xchan'] = get_observer_hash(); // The author is either the owner or whomever was specified @@ -1425,7 +1419,7 @@ function scan_webpage_elements($path, $type, $cloud = false) { 'application/x-pdl', 'application/x-php' ]; - // Blocks and pages can have any mimetype, but layouts must be text/bbcode + // Blocks and pages can have any of the valid mimetypes, but layouts must be text/bbcode if((in_array($element['mimetype'], $mimetypes)) && ($type === 'page' || $type === 'block') ) { $arr['mimetype'] = $element['mimetype']; } else { @@ -1434,7 +1428,7 @@ function scan_webpage_elements($path, $type, $cloud = false) { // Verify ability to use html or php!!! $execflag = false; - if ($arr['mimetype'] === 'application/x-php') { + if ($arr['mimetype'] === 'application/x-php' || $arr['mimetype'] === 'text/html') { $z = q("select account_id, account_roles, channel_pageflags from account " . "left join channel on channel_account_id = account_id where channel_id = %d limit 1", intval(local_channel()) @@ -1442,10 +1436,15 @@ function scan_webpage_elements($path, $type, $cloud = false) { if ($z && (($z[0]['account_roles'] & ACCOUNT_ROLE_ALLOWCODE) || ($z[0]['channel_pageflags'] & PAGE_ALLOWCODE))) { $execflag = true; + } else { + logger('Unable to import element "' . $name .'" because AllowCode permission is denied.'); + notice( t('Unable to import element "' . $name .'" because AllowCode permission is denied.') . EOL); + $element['import_success'] = 0; + return $element; } } - $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'service' limit 1", + $z = q("select * from iconfig where v = '%s' and k = '%s' and cat = 'system' limit 1", dbesc($name), dbesc($namespace) ); -- cgit v1.2.3 From 5994fadebbbc9b12137d23eb52f6218df53cf613 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 30 Aug 2016 20:22:05 -0700 Subject: version and release policy guide --- doc/project/versions.bb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 doc/project/versions.bb diff --git a/doc/project/versions.bb b/doc/project/versions.bb new file mode 100644 index 000000000..451cd0448 --- /dev/null +++ b/doc/project/versions.bb @@ -0,0 +1,30 @@ +[h2]Versions and Releases[/h2] + +$Projectname currently uses a standard version numbering sequence of $x.$y(.$z), for instance '1.12' or '1.12.1'. The first digit is the major version number. Major versions are released "roughly" once per year; often in December. + +The second digit is the minor release number. If this number is odd, it is a development version. If the number is even, it is a released version. Minor versions are released (moved from dev to master) typically once per month when development is 'stable', but this is likely to increase. Going forward minor releases will be made somewhere between one and three months; corresponding to a stable code point and when there is general community consensus that the current code base is stable enough to consider a release. + +The final digit is an interface or patch designator. + +The release process involves changing the version number (by definition the minor version number will be odd, and the minor number will be incremented). Once a year for a major release the major version will be incremented, and the minor number reset to 0. + +The release candidate is moved to a new branch; and testing will commence/continue for a period of 1-2 weeks afterward or until any significant issues have been resolved. This branch is usually labelled with RC (release candidate); for instance 1.8RC represents the pending release of version 1.8. At this time, the minor version number on the dev branch is incremented to the next odd number. (For instance 1.9). New development can then take place in the dev branch. + +Bug fixes should always be applied to 'dev' and from there merged forward (typically with git cherry-pick) to the RC branch and if necessary applied to the master or official release branch. + +At the time a release candidate is produced, the language strings file is frozen until a release is made. Translation work may continue, but all translations should be submitted to 'dev' and merged forward to RC. + + +Once RC testing is completed, RC is merged to 'master' and the RC version designator removed; resulting in one final checkin to change the version number. The CHANGELOG file should also be updated at or just prior to this time. If there are merge conflicts during this final merge, the merge will be abandoned; and 'git merge -s ours' applied. This results in a replacement of master with the contents of the RC branch. Conflicts often arise with string updates which were made to master after the last release and cannot easily be resolved without hand editing. Since this is a release of tested code, hand editing is discouraged, and the replacement merge strategy should be used instead. It is assumed that RC now contains the most recent well-tested code. + +Once the release is live and merged to master, the RC branch may be removed. + +Fixes may be made to master after release. Where possible these should be made to dev and 'git cherry-pick' used to merge forward; which preserves the commit info and prevents merge conflicts in the next cycle. Only rarely does a patch only apply to the master branch. If necessary this can be made. If the change is severe, the interface version number should be incremented. This is at the discretion of the community. In any event, a 'git pull' of the master branch should always result in the latest release with any post-release patches applied. + +The interface number (the $z in $x.$y.$z) should be incremented in dev whenever a change is made which changes the interfaces or API in incompatible ways so that any external packages (especially addons and API clients) relying on a the current behaviour can discover and change their own interfaces accordingly at the point that it changed. + + + + + + \ No newline at end of file -- cgit v1.2.3 From bf3a4095694808486106fcce7cc371ce5c13f587 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 30 Aug 2016 20:54:54 -0700 Subject: only check permissions on normal photos and force cover photos as well as profile photos to be public. As a side effect 'thing' photos will also be considered public. --- Zotlabs/Module/Photo.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Zotlabs/Module/Photo.php b/Zotlabs/Module/Photo.php index 66aaec49f..a16206299 100644 --- a/Zotlabs/Module/Photo.php +++ b/Zotlabs/Module/Photo.php @@ -133,7 +133,16 @@ class Photo extends \Zotlabs\Web\Controller { $allowed = (($r[0]['uid']) ? perm_is_allowed($r[0]['uid'],$observer_xchan,'view_storage') : true); $sql_extra = permissions_sql($r[0]['uid']); + + if(! $sql_extra) + $sql_extra = ' and true '; + + // Only check permissions on normal photos. Those photos we don't check includes + // profile photos, xchan photos (which are also profile photos), 'thing' photos, + // and cover photos + $sql_extra = " and (( photo_usage = 0 $sql_extra ) or photo_usage != 0 )"; + $channel = channelx_by_n($r[0]['uid']); // Now we'll see if we can access the photo -- cgit v1.2.3 From b775a1aa0e36a74f0f937d85f458fd12af18a264 Mon Sep 17 00:00:00 2001 From: redmatrix Date: Tue, 30 Aug 2016 21:43:33 -0700 Subject: The 'save to folder' modal dialog looked like crap. Now it only looks like dung. --- view/css/conversation.css | 8 ++++++++ view/tpl/field_combobox.tpl | 10 +++++----- view/tpl/filer_dialog.tpl | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/view/css/conversation.css b/view/css/conversation.css index 6c5171545..677198149 100644 --- a/view/css/conversation.css +++ b/view/css/conversation.css @@ -319,3 +319,11 @@ img.smiley.emoji:hover { width: 32px; height: 32px; } + +.combobox { + padding: 15px; +} + +#filer_save { + margin-left: 15px; +} \ No newline at end of file diff --git a/view/tpl/field_combobox.tpl b/view/tpl/field_combobox.tpl index 337c60673..523595c0b 100755 --- a/view/tpl/field_combobox.tpl +++ b/view/tpl/field_combobox.tpl @@ -1,13 +1,13 @@ -
+
- {{* html5 don't work on Chrome, Safari and IE9 see https://github.com/thgreasi/datalist-polyfill + {{* {{foreach $field.4 as $opt=>$val}} *}} + *}} - - + diff --git a/view/tpl/filer_dialog.tpl b/view/tpl/filer_dialog.tpl index ae59ab713..71e075958 100755 --- a/view/tpl/filer_dialog.tpl +++ b/view/tpl/filer_dialog.tpl @@ -1,4 +1,6 @@ +
{{include file="field_combobox.tpl"}}
+
-- cgit v1.2.3