aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Zotlabs/Lib/ExtendedZip.php57
-rw-r--r--Zotlabs/Module/Apps.php1
-rw-r--r--Zotlabs/Module/Photo.php9
-rw-r--r--Zotlabs/Module/Webpages.php318
-rw-r--r--doc/general.bb2
-rw-r--r--doc/project/governance.bb45
-rw-r--r--doc/project/versions.bb30
-rw-r--r--include/attach.php46
-rw-r--r--include/import.php215
-rwxr-xr-xinclude/oembed.php15
-rw-r--r--include/text.php16
-rw-r--r--include/widgets.php4
-rw-r--r--view/css/conversation.css8
-rw-r--r--view/css/mod_webpages.css74
-rw-r--r--view/pdl/mod_webpages.pdl2
-rwxr-xr-xview/tpl/field_combobox.tpl10
-rwxr-xr-xview/tpl/filer_dialog.tpl2
-rw-r--r--view/tpl/webpage_export_list.tpl124
-rw-r--r--view/tpl/website_import_tools.tpl37
-rw-r--r--view/tpl/website_portation_tools.tpl72
20 files changed, 1019 insertions, 68 deletions
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 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+namespace Zotlabs\Lib;
+
+/**
+ * Description of ExtendedZip
+ *
+ * @author andrew
+ */
+class ExtendedZip extends \ZipArchive {
+
+ // Member function to add a whole file system subtree to the archive
+ public function addTree($dirname, $localname = '') {
+ if ($localname)
+ $this->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/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 @@
<?php
namespace Zotlabs\Module;
-//require_once('include/apps.php');
use \Zotlabs\Lib as Zlib;
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
diff --git a/Zotlabs/Module/Webpages.php b/Zotlabs/Module/Webpages.php
index 0a48d43c6..acaca16c3 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();
@@ -62,6 +61,28 @@ class Webpages extends \Zotlabs\Web\Controller {
case 'importselected':
$_SESSION['action'] = null;
break;
+ case 'export_select_list':
+ $_SESSION['action'] = null;
+ if(!$uid) {
+ $_SESSION['export'] = null;
+ break;
+ }
+ require_once('include/import.php');
+
+ $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;
+
default :
$_SESSION['action'] = null;
break;
@@ -233,7 +254,6 @@ class Webpages extends \Zotlabs\Web\Controller {
}
function post() {
-
$action = $_REQUEST['action'];
if( $action ){
switch ($action) {
@@ -313,7 +333,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;
@@ -381,16 +402,299 @@ 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':
+
+ if(isset($_POST['w_download'])) {
+ $_SESSION['action'] = 'export_select_list';
+ $_SESSION['export'] = 'zipfile';
+ if(isset($_POST['zipfilename']) && $_POST['zipfilename'] !== '') {
+ $filename = filter_var($_POST['zipfilename'], FILTER_SANITIZE_ENCODED);
+ } else {
+ $filename = 'website.zip';
+ }
+ $_SESSION['zipfilename'] = $filename;
+
+ }
+
+ break;
+
+ case 'exportcloud':
+ 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();
+
+ $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;
+
+ $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)) {
+ 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'],
+ 'mimetype' => $l['mimetype'],
+ )
+ );
+ 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) {
+
+ $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']) {
+ $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 (!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));
+ }
+ }
+ 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 (!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'));
+ }
+ file_put_contents($page_filepath, $pageinfo['body']);
+ file_put_contents($page_jsonpath, json_encode($pageinfo['json'], JSON_UNESCAPED_SLASHES));
+ }
+ }
+ }
+ }
+ 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/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.
+
+
+
+
+
+
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
diff --git a/include/attach.php b/include/attach.php
index ae7d71dc7..137d2b11c 100644
--- a/include/attach.php
+++ b/include/attach.php
@@ -1995,6 +1995,52 @@ 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;
+}
/**
* attach_move()
* This function performs an in place directory-to-directory move of a stored attachment or photo.
diff --git a/include/import.php b/include/import.php
index 84881a420..ddffb2ac3 100644
--- a/include/import.php
+++ b/include/import.php
@@ -1312,8 +1312,12 @@ function scan_webpage_elements($path, $type, $cloud = false) {
}
$content = file_get_contents($folder . '/' . $contentfilename);
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;
}
@@ -1395,12 +1399,12 @@ 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']);
// The element owner is the channel importing the elements
@@ -1415,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 {
@@ -1424,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())
@@ -1432,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)
);
@@ -1472,3 +1481,193 @@ 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 'all':
+ // If all, execute all the pages, layouts, blocks case statements
+ 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;
+ }
+
+ }
+ 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;
+ }
+
+ 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;
+ }
+ return $elements;
+}
+
+/* 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/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;
}
}
diff --git a/include/text.php b/include/text.php
index a2a6d918b..99ac59ca7 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,7 +2290,15 @@ 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'),
+ '$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.'),
+ '$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/include/widgets.php b/include/widgets.php
index 765cb4d42..9429df249 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,7 +791,7 @@ function widget_website_import_tools($arr) {
if(! local_channel())
return '';
- return website_import_tools();
+ return website_portation_tools();
}
function widget_findpeople($arr) {
diff --git a/view/css/conversation.css b/view/css/conversation.css
index fe89e2536..6acca4b0d 100644
--- a/view/css/conversation.css
+++ b/view/css/conversation.css
@@ -324,3 +324,11 @@ input.listcheckbox {
margin: 0px;
vertical-align: middle;
}
+
+.combobox {
+ padding: 15px;
+}
+
+#filer_save {
+ margin-left: 15px;
+}
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/pdl/mod_webpages.pdl b/view/pdl/mod_webpages.pdl
index b62ec6e7c..9e4d604ba 100644
--- a/view/pdl/mod_webpages.pdl
+++ b/view/pdl/mod_webpages.pdl
@@ -1,4 +1,4 @@
[region=aside]
[widget=design_tools][/widget]
-[widget=website_import_tools][/widget]
+[widget=website_portation_tools][/widget]
[/region] \ 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 @@
- <div class='field combobox'>
+ <div class='field combobox input form-group'>
<label class="mainlabel" for='id_{{$field.0}}' id='id_{{$field.0}}_label'>{{$field.1}}</label>
- {{* html5 don't work on Chrome, Safari and IE9 see https://github.com/thgreasi/datalist-polyfill
+ {{*
<input id="id_{{$field.0}}" type="text" list="data_{{$field.0}}" >
<datalist id="data_{{$field.0}}" >
{{foreach $field.4 as $opt=>$val}}<option value="{{$val}}">{{/foreach}}
- </datalist> *}}
+ </datalist>*}}
- <input id="id_{{$field.0}}" type="text" value="{{$field.2}}">
- <select id="select_{{$field.0}}" onChange="$('#id_{{$field.0}}').val($(this).val())">
+ <input id="id_{{$field.0}}" class="form-control" type="text" value="{{$field.2}}">
+ <select id="select_{{$field.0}}" class="form-control" onChange="$('#id_{{$field.0}}').val($(this).val())">
<option value="">{{$field.5}}</option>
{{foreach $field.4 as $opt=>$val}}<option value="{{$val}}">{{$val}}</option>{{/foreach}}
</select>
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 @@
+<div class="item-filer-dialog">
{{include file="field_combobox.tpl"}}
<div class="settings-submit-wrapper" >
<input id="filer_save" type="button" class="settings-submit" value="{{$submit}}" />
</div>
+</div>
diff --git a/view/tpl/webpage_export_list.tpl b/view/tpl/webpage_export_list.tpl
new file mode 100644
index 000000000..1d28f62df
--- /dev/null
+++ b/view/tpl/webpage_export_list.tpl
@@ -0,0 +1,124 @@
+<div class="generic-content-wrapper">
+ <form action="" method="post" autocomplete="on" >
+ <input type="hidden" name="action" value="{{$action}}">
+ <div class="section-title-wrapper">
+ <div class="pull-right">
+ <button class="btn btn-md btn-success" type="submit" name="submit" value="{{$exportbtn}}">{{$exportbtn}}</button>
+ </div>
+ <h2>{{$title}}</h2>
+ <div class="clear"></div>
+ </div>
+ <div id="import-website-content-wrapper" class="section-content-wrapper">
+ <div class="pull-left">
+ <button id="toggle-select-all" class="btn btn-xs btn-primary" onclick="checkedAll(window.isChecked); return false;"><i class="fa fa-check"></i>&nbsp;Toggle Select All</button>
+ </div>
+ <div class="clear"></div>
+ <hr>
+ <!--<h4>Pages</h4>-->
+ <div>
+ <table class="table-striped table-responsive table-hover" style="width: 100%;">
+ <thead>
+ <tr><th></th><th>Page Link</th><th>Page Title</th><th>Type</th></tr>
+ </thead>
+ {{foreach $pages as $page}}
+ <tr>
+ <td width="30px" style="padding-right: 20px;">
+ <div class='squaredThree'>
+ <input type="checkbox" id="page_{{$page.mid}}" name="page[]" value="{{$page.mid}}">
+ <label for="page_{{$page.mid}}"></label>
+ </div>
+ </td>
+ <td width="30%">
+ <div class="desc">
+ {{$page.pagetitle}}<br>
+ </div>
+ </td>
+ <td width="55%">
+ <div class='desc'>
+ {{$page.title}}<br>
+ </div>
+ </td>
+ <td width="15%">
+ <div class='desc'>
+ {{$page.mimetype}}<br>
+ </div>
+ </td>
+ </tr>
+ {{/foreach}}
+ </table>
+ </div>
+ <hr>
+ <div class="clear"></div>
+ <!--<h4>Layouts</h4>-->
+ <div>
+ <table class="table-striped table-responsive table-hover" style="width: 100%;">
+ <thead>
+ <tr><th width="20px"></th><th>Layout Name</th><th>Layout Description</th><th>Type</th></tr>
+ </thead>
+ {{foreach $layouts as $layout}}
+ <tr>
+ <td width="30px" style="padding-right: 20px;">
+ <div class='squaredThree'>
+ <input type="checkbox" id="layout_{{$layout.mid}}" name="layout[]" value="{{$layout.mid}}">
+ <label for="layout_{{$layout.mid}}"></label>
+ </div>
+ </td>
+ <td width="30%">
+ <div class="desc">
+ {{$layout.name}}<br>
+ </div>
+ </td>
+ <td width="55%">
+ <div class='desc'>
+ {{$layout.description}}<br>
+ </div>
+ </td>
+ <td width="15%">
+ <div class='desc'>
+ {{$layout.mimetype}}<br>
+ </div>
+ </td>
+ </tr>
+ {{/foreach}}
+ </table>
+ </div>
+ <hr>
+ <div class="clear"></div>
+ <!--<h4>Blocks</h4>-->
+ <div>
+ <table class="table-striped table-responsive table-hover" style="width: 100%;">
+ <thead>
+ <tr><th width="30px"></th><th>Block Name</th><th>Block Title</th><th>Type</th></tr>
+ </thead>
+ {{foreach $blocks as $block}}
+ <tr>
+ <td width="30px" style="padding-right: 20px;">
+ <div class='squaredThree'>
+ <input type="checkbox" id="block_{{$block.mid}}" name="block[]" value="{{$block.mid}}">
+ <label for="block_{{$block.mid}}"></label>
+ </div>
+ </td>
+ <td width="30%">
+ <div class="desc">
+ {{$block.name}}<br>
+ </div>
+ </td>
+ <td width="55%">
+ <div class='desc'>
+ {{$block.title}}<br>
+ </div>
+ </td>
+ <td width=15%">
+ <div class='desc'>
+ {{$block.mimetype}}<br>
+ </div>
+ </td>
+ </tr>
+ {{/foreach}}
+ </table>
+ </div>
+
+ </div>
+ </form>
+</div>
+
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 @@
-<div id="website-import-tools" class="widget">
- <h3>{{$title}}</h3>
- <ul class="nav nav-pills nav-stacked">
- <li>
- <a href="#" onclick="openClose('import-form');
- return false;"><i class="fa fa-cloud-upload generic-icons"></i> {{$import_label}}</a>
- </li>
- <li>
- <form id="import-form" enctype="multipart/form-data" method="post" action="" style="display: none;" class="sub-menu">
-
- <input type="hidden" name="action" value="scan">
-
- <p style="margin-top: 20px;" class="descriptive-text">{{$file_import_text}}</p>
- <div class="form-group">
- <div class="input-group">
- <input class="widget-input" type="text" name="path" title="{{$hint}}" placeholder="{{$desc}}" />
- <div class="input-group-btn">
- <button class="btn btn-default btn-sm" type="submit" name="cloudsubmit" value="{{$select}}"><i class="fa fa-folder-open generic-icons"></i></button>
- </div>
- </div>
- </div>
-
- <!-- Or upload a zipped file containing the website -->
- <p class="descriptive-text">{{$file_upload_text}}</p>
- <div class="form-group">
-
- <div class="input-group">
- <input class="widget-input" type="file" name="zip_file" />
- <div class="input-group-btn">
- <button class="btn btn-default btn-sm" type="submit" name="w_upload" value="w_upload"><i class="fa fa-file-archive-o generic-icons"></i></button>
- </div>
- </div>
- </div>
- </form>
- </li>
- </ul>
-</div>
diff --git a/view/tpl/website_portation_tools.tpl b/view/tpl/website_portation_tools.tpl
new file mode 100644
index 000000000..10468b64e
--- /dev/null
+++ b/view/tpl/website_portation_tools.tpl
@@ -0,0 +1,72 @@
+<div id="website-portation-tools" class="widget">
+ <ul class="nav nav-pills nav-stacked">
+ <li>
+ <a href="#" onclick="openClose('import-form');
+ return false;"><i class="fa fa-cloud-upload generic-icons"></i> {{$import_label}}</a>
+ </li>
+ <li style="margin-left: 12px;" >
+ <form id="import-form" enctype="multipart/form-data" method="post" action="" style="display: none;" class="sub-menu">
+
+ <input type="hidden" name="action" value="scan">
+
+ <p style="margin-top: 10px;" class="descriptive-text">{{$file_import_text}}</p>
+ <div class="form-group">
+ <div class="input-group">
+ <input class="widget-input" type="text" name="path" title="{{$hint}}" placeholder="{{$desc}}" />
+ <div class="input-group-btn">
+ <button class="btn btn-default btn-sm" type="submit" name="cloudsubmit" value="{{$select}}"><i class="fa fa-folder-open generic-icons"></i></button>
+ </div>
+ </div>
+ </div>
+
+ <!-- Or upload a zipped file containing the website -->
+ <p class="descriptive-text">{{$file_upload_text}}</p>
+ <div class="form-group">
+
+ <div class="input-group">
+ <input class="widget-input" type="file" name="zip_file" />
+ <div class="input-group-btn">
+ <button class="btn btn-default btn-sm" type="submit" name="w_upload" value="w_upload"><i class="fa fa-file-archive-o generic-icons"></i></button>
+ </div>
+ </div>
+ </div>
+ </form>
+ </li>
+ </ul>
+ <ul class="nav nav-pills nav-stacked">
+ <li>
+ <a href="#" onclick="openClose('export-form'); openClose('export-cloud-form');
+ return false;"><i class="fa fa-share-square-o generic-icons"></i> {{$export_label}}</a>
+ </li>
+ <li style="margin-left: 12px;" >
+ <form id="export-form" enctype="multipart/form-data" method="post" action="" style="display: none;" class="sub-menu">
+ <input type="hidden" name="action" value="exportzipfile">
+ <!-- Or download a zipped file containing the website -->
+ <p style="margin-top: 10px;" class="descriptive-text">{{$file_download_text}}</p>
+ <div class="form-group">
+
+ <div class="input-group">
+ <input class="widget-input" type="text" name="zipfilename" title="{{$filename_hint}}" placeholder="{{$filename_desc}}" value="" />
+ <div class="input-group-btn">
+ <button class="btn btn-default btn-sm" type="submit" name="w_download" value="w_download"><i class="fa fa-download generic-icons"></i></button>
+ </div>
+ </div>
+ </div>
+ </form>
+ <form id="export-cloud-form" enctype="multipart/form-data" method="post" action="" style="display: none;" class="sub-menu">
+ <input type="hidden" name="action" value="exportcloud">
+ <!-- Or export the website elements to a cloud files folder -->
+ <p style="margin-top: 10px;" class="descriptive-text">{{$cloud_export_text}}</p>
+ <div class="form-group">
+
+ <div class="input-group">
+ <input class="widget-input" type="text" name="exportcloudpath" title="{{$cloud_export_hint}}" placeholder="{{$cloud_export_desc}}" />
+ <div class="input-group-btn">
+ <button class="btn btn-default btn-sm" type="submit" name="exportcloudsubmit" value="{{$cloud_export_select}}"><i class="fa fa-folder-open generic-icons"></i></button>
+ </div>
+ </div>
+ </div>
+ </form>
+ </li>
+ </ul>
+</div>