diff options
26 files changed, 1049 insertions, 41 deletions
diff --git a/Zotlabs/Module/Pdledit_gui.php b/Zotlabs/Module/Pdledit_gui.php new file mode 100644 index 000000000..b550b92d3 --- /dev/null +++ b/Zotlabs/Module/Pdledit_gui.php @@ -0,0 +1,553 @@ +<?php + +namespace Zotlabs\Module; + +use App; +use Zotlabs\Web\Controller; +use Zotlabs\Render\Comanche; +use Zotlabs\Lib\Libsync; + +class Pdledit_gui extends Controller { + + function post() { + + if (!local_channel()) { + return; + } + + if (!$_REQUEST['module']) { + return; + } + + $module = $_REQUEST['module']; + + $ret = [ + 'success' => false, + 'module' => $module + ]; + + if ($_REQUEST['reset']) { + del_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl'); + Libsync::build_sync_packet(); + $ret['success'] = true; + json_return_and_die($ret); + } + + if ($_REQUEST['save']) { + if (!$_REQUEST['data']) { + return $ret; + } + + $data = json_decode($_REQUEST['data'],true); + $stored_pdl_result = self::get_pdl($module); + $pdl = $stored_pdl_result['pdl']; + + foreach ($data as $region => $entries) { + $region_pdl = ''; + foreach ($entries as $entry) { + $region_pdl .= base64_decode($entry) . "\r\n"; + } + $pdl = preg_replace('/\[region=' . $region . '\](.*?)\[\/region\]/ism', '[region=' . $region . ']' . "\r\n" . $region_pdl . "\r\n" . '[/region]', $pdl); + } + + set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($pdl)); + Libsync::build_sync_packet(); + + $ret['success'] = true; + json_return_and_die($ret); + } + + if ($_REQUEST['save_src']) { + set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($_REQUEST['src'])); + Libsync::build_sync_packet(); + + $ret['success'] = true; + json_return_and_die($ret); + } + + if ($_REQUEST['save_template']) { + if (!$_REQUEST['data']) { + return $ret; + } + + $template = $_REQUEST['data'][0]['value']; + $pdl_result = self::get_pdl($module); + $stored_template = self::get_template($pdl_result['pdl']); + + if ($template === $stored_template) { + $ret['success'] = true; + return $ret; + } + + $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $pdl_result['pdl'], $matches); + if ($cnt) { + $pdl = str_replace('[template]' . $stored_template . '[/template]', '[template]' . $template . '[/template]', $pdl_result['pdl']); + } + else { + $pdl = '[template]' . $template . '[/template]' . "\r\n"; + $pdl .= $pdl_result['pdl']; + } + + set_pconfig(local_channel(), 'system', 'mod_' . $module . '.pdl', escape_tags($pdl)); + Libsync::build_sync_packet(); + + $ret['success'] = true; + json_return_and_die($ret); + } + + } + + function get() { + + if(! local_channel()) { + return EMPTY_STR; + } + + $module = argv(1); + + if (!$module) { + goaway(z_root() . '/pdledit_gui/hq'); + } + + $pdl_result = self::get_pdl($module); + + $pdl = $pdl_result['pdl']; + $modified = $pdl_result['modified']; + + if(!$pdl) { + return t('Layout not found'); + } + + $template = self::get_template($pdl); + + $template_info = self::get_template_info($template); + + if(empty($template_info['contentregion'])) { + return t('This template does not support pdledi_gui (no content regions defined)'); + } + + App::$page['template'] = $template; + + $regions = self::get_regions($pdl); + + foreach ($regions as $k => $v) { + $region_str = ''; + if (is_array($v)) { + ksort($v); + foreach ($v as $entry) { + // Get the info from the file and replace entry if we get anything useful + $widget_info = get_widget_info($entry['name']); + $entry['name'] = (($widget_info['name']) ? $widget_info['name'] : $entry['name']); + $entry['desc'] = (($widget_info['description']) ? $widget_info['description'] : $entry['desc']); + + $region_str .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [ + '$entry' => $entry + ]); + } + } + App::$layout['region_' . $k] = $region_str; + } + + $templates = self::get_templates(); + $templates_html = replace_macros(get_markup_template('pdledit_gui_templates.tpl'), [ + '$templates' => $templates, + '$active' => $template + ]); + + $items_html = ''; + + //$items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [ + //'$entry' => [ + //'type' => 'content', + //'name' => t('Main page content'), + //'src' => base64_encode('$content') + //], + //'$disable_controls' => true + //]); + + foreach (self::get_widgets($module) as $entry) { + $items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [ + '$entry' => $entry, + '$disable_controls' => true + ]); + } + + foreach (self::get_menus() as $entry) { + $items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [ + '$entry' => $entry, + '$disable_controls' => true + ]); + } + + foreach (self::get_blocks() as $entry) { + $items_html .= replace_macros(get_markup_template('pdledit_gui_item.tpl'), [ + '$entry' => $entry, + '$disable_controls' => true + ]); + } + + App::$layout['region_content'] .= replace_macros(get_markup_template('pdledit_gui.tpl'), [ + '$content_regions' => $template_info['contentregion'], + '$page_src' => base64_encode($pdl), + '$templates' => base64_encode($templates_html), + '$modules' => base64_encode(self::get_modules()), + '$items' => base64_encode($items_html), + '$module_modified' => $modified, + '$module' => $module + ]); + + } + + function get_templates() { + $ret = []; + + $files = glob('view/php/*.php'); + if($files) { + foreach($files as $f) { + $name = basename($f, '.php'); + $x = get_template_info($name); + if(!empty($x['contentregion'])) { + $ret[] = [ + 'name' => $name, + 'desc' => $x['description'] + ]; + } + } + } + + return $ret; + } + + function get_modules() { + $ret = ''; + + $files = glob('Zotlabs/Module/*.php'); + if($files) { + foreach($files as $f) { + $name = lcfirst(basename($f,'.php')); + + if ($name === 'admin' && !is_site_admin()) { + continue; + } + + $x = theme_include('mod_' . $name . '.pdl'); + if($x) { + $ret .= '<div class="mb-2"><a href="pdledit_gui/' . $name . '">' . $name . '</a></div>'; + } + } + } + + return $ret; + } + + function get_widgets($module) { + $ret = []; + + $checkpaths = [ + 'Zotlabs/Widget/*.php' + ]; + + foreach ($checkpaths as $path) { + $files = glob($path); + if($files) { + foreach($files as $f) { + $name = lcfirst(basename($f, '.php')); + + $widget_info = get_widget_info($name); + if ($widget_info['requires'] && strpos($widget_info['requires'], 'admin') !== false && !is_site_admin()) { + continue; + } + + if ($widget_info['requires'] && strpos($widget_info['requires'], $module) === false) { + continue; + } + + $ret[] = [ + 'type' => 'widget', + 'name' => $widget_info['name'] ?? $name, + 'desc' => $widget_info['description'] ?? '', + 'src' => base64_encode('[widget=' . $name . '][/widget]') + ]; + } + } + } + + return $ret; + } + + function get_menus() { + $ret = []; + + $r = q("select * from menu where menu_channel_id = %d and menu_flags = 0", + intval(local_channel()) + ); + + foreach ($r as $rr) { + $name = $rr['menu_name']; + $desc = $rr['menu_desc']; + $ret[] = [ + 'type' => 'menu', + 'name' => $name, + 'desc' => $desc, + 'src' => base64_encode('[menu]' . $name . '[/menu]') + ]; + } + + return $ret; + } + + function get_blocks() { + $ret = []; + + $r = q("select v, title, summary from item join iconfig on iconfig.iid = item.id and item.uid = %d + and iconfig.cat = 'system' and iconfig.k = 'BUILDBLOCK'", + intval(local_channel()) + ); + + foreach ($r as $rr) { + $name = $rr['v']; + $desc = (($rr['title']) ? $rr['title'] : $rr['summary']); + $ret[] = [ + 'type' => 'block', + 'name' => $name, + 'desc' => $desc, + 'src' => base64_encode('[block]' . $name . '[/block]') + ]; + } + + return $ret; + } + + function get_template($pdl) { + $ret = 'default'; + + $cnt = preg_match("/\[template\](.*?)\[\/template\]/ism", $pdl, $matches); + if($cnt && isset($matches[1])) { + $ret = trim($matches[1]); + } + + return $ret; + } + + function get_regions($pdl) { + $ret = []; + $supported_regions = ['aside', 'content', 'right_aside']; + + $cnt = preg_match_all("/\[region=(.*?)\](.*?)\[\/region\]/ism", $pdl, $matches, PREG_SET_ORDER); + if($cnt) { + foreach($matches as $mtch) { + if (!in_array($mtch[1], $supported_regions)) { + continue; + } + $ret[$mtch[1]] = self::parse_region($mtch[2]); + } + } + + return $ret; + } + + function parse_region($pdl) { + $ret = []; + + $cnt = preg_match_all('/\$content\b/ism', $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + if($cnt) { + foreach($matches as $mtch) { + $offset = intval($mtch[0][1]); + $name = trim($mtch[0][0]); + //$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0])); + $src = base64_encode($mtch[0][0]); + $ret[$offset] = [ + 'type' => 'content', + 'name' => t('Main page content'), + 'desc' => t('The main page content can not be edited!'), + 'src' => $src + ]; + } + } + + $cnt = preg_match_all("/\[menu\](.*?)\[\/menu\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + if($cnt) { + foreach($matches as $mtch) { + $offset = intval($mtch[1][1]); + $name = trim($mtch[1][0]); + //$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0])); + $src = base64_encode($mtch[0][0]); + + $ret[$offset] = [ + 'type' => 'menu', + 'name' => $name, + 'desc' => '', + 'src' => $src + ]; + } + } + + // menu class e.g. [menu=horizontal]my_menu[/menu] or [menu=tabbed]my_menu[/menu] + // allows different menu renderings to be applied + + //$cnt = preg_match_all("/\[menu=(.*?)\](.*?)\[\/menu\]/ism", $s, $matches, PREG_SET_ORDER); + //if($cnt) { + //foreach($matches as $mtch) { + //$s = str_replace($mtch[0],$this->menu(trim($mtch[2]),$mtch[1]),$s); + //} + //} + + + $cnt = preg_match_all("/\[block\](.*?)\[\/block\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + if($cnt) { + foreach($matches as $mtch) { + $offset = intval($mtch[1][1]); + $name = trim($mtch[1][0]); + //$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0])); + $src = base64_encode($mtch[0][0]); + $ret[$offset] = [ + 'type' => 'block', + 'name' => $name, + 'desc' => '', + 'src' => $src + ]; + } + } + + //$cnt = preg_match_all("/\[block=(.*?)\](.*?)\[\/block\]/ism", $s, $matches, PREG_SET_ORDER); + //if($cnt) { + //foreach($matches as $mtch) { + //$s = str_replace($mtch[0],$this->block(trim($mtch[2]),trim($mtch[1])),$s); + //} + //} + + //$cnt = preg_match_all("/\[js\](.*?)\[\/js\]/ism", $s, $matches, PREG_SET_ORDER); + //if($cnt) { + //foreach($matches as $mtch) { + //$s = str_replace($mtch[0],$this->js(trim($mtch[1])),$s); + //} + //} + + //$cnt = preg_match_all("/\[css\](.*?)\[\/css\]/ism", $s, $matches, PREG_SET_ORDER); + //if($cnt) { + //foreach($matches as $mtch) { + //$s = str_replace($mtch[0],$this->css(trim($mtch[1])),$s); + //} + //} + + // need to modify this to accept parameters + + $cnt = preg_match_all("/\[widget=(.*?)\](.*?)\[\/widget\]/ism", $pdl, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE); + + if($cnt) { + foreach($matches as $mtch) { + $offset = intval($mtch[1][1]); + $name = trim($mtch[1][0]); + //$src = base64url_encode(preg_replace(['/\s*\[/', '/\]\s*/'], ['[', ']'], $mtch[0][0])); + $src = base64_encode($mtch[0][0]); + $ret[$offset] = [ + 'type' => 'widget', + 'name' => $name, + 'desc' => '', + 'src' => $src + ]; + } + } + + return $ret; + + } + + + /** + * @brief Parse template comment in search of template info. + * + * like + * \code + * * Name: MyWidget + * * Description: A widget + * * Version: 1.2.3 + * * Author: John <profile url> + * * Author: Jane <email> + * * ContentRegionID: some_id + * * ContentRegionID: some_other_id + * * + *\endcode + * @param string $template the name of the template + * @return array with the information + */ + function get_template_info($template){ + $m = []; + $info = [ + 'name' => $template, + 'description' => '', + 'author' => [], + 'maintainer' => [], + 'version' => '', + 'contentregion' => [] + ]; + + $checkpaths = [ + 'view/php/' . $template . '.php', + ]; + + $template_found = false; + + foreach ($checkpaths as $path) { + if (is_file($path)) { + $template_found = true; + $f = file_get_contents($path); + break; + } + } + + if(!($template_found && $f)) + return $info; + + $f = escape_tags($f); + $r = preg_match('|/\*.*\*/|msU', $f, $m); + + if ($r) { + $ll = explode("\n", $m[0]); + foreach($ll as $l) { + $l = trim($l, "\t\n\r */"); + if ($l != ''){ + list($k, $v) = array_map('trim', explode(':', $l, 2)); + $k = strtolower($k); + if (in_array($k, ['author', 'maintainer'])){ + $r = preg_match('|([^<]+)<([^>]+)>|', $v, $m); + if ($r) { + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info[$k][] = array('name' => $v); + } + } + elseif (in_array($k, ['contentregion'])){ + $info[$k][] = array_map('trim', explode(',', $v)); + } + else { + $info[$k] = $v; + } + } + } + } + + return $info; + } + + function get_pdl($module) { + $ret = [ + 'pdl' => null, + 'modified' => true + ]; + + $pdl_path = 'mod_' . $module . '.pdl'; + + $ret['pdl'] = get_pconfig(local_channel(), 'system', $pdl_path); + + if(!$ret['pdl']) { + $pdl_path = theme_include($pdl_path); + if ($pdl_path) { + $ret['pdl'] = file_get_contents($pdl_path); + $ret['modified'] = false; + } + } + + return $ret; + } +} diff --git a/Zotlabs/Web/WebServer.php b/Zotlabs/Web/WebServer.php index de0d5a883..70c6eb9b8 100644 --- a/Zotlabs/Web/WebServer.php +++ b/Zotlabs/Web/WebServer.php @@ -107,7 +107,8 @@ class WebServer { $Router->Dispatch(); - $this->set_homebase(); + // TODO: this is not used for anything atm and messes up comanche templates by adding some javascript + //$this->set_homebase(); // now that we've been through the module content, see if the page reported // a permission problem and if so, a 403 response would seem to be in order. diff --git a/Zotlabs/Widget/Activity_filter.php b/Zotlabs/Widget/Activity_filter.php index b7a69752e..daaf5fb67 100644 --- a/Zotlabs/Widget/Activity_filter.php +++ b/Zotlabs/Widget/Activity_filter.php @@ -1,5 +1,12 @@ <?php +/** + * * Name: Activity filters + * * Description: Filters for the network stream + * * Requires: network + */ + + namespace Zotlabs\Widget; use App; diff --git a/Zotlabs/Widget/Activity_order.php b/Zotlabs/Widget/Activity_order.php index d3fe2a30f..e8ee11508 100644 --- a/Zotlabs/Widget/Activity_order.php +++ b/Zotlabs/Widget/Activity_order.php @@ -2,6 +2,12 @@ namespace Zotlabs\Widget; +/** + * * Name: Activity order + * * Description: Order the network stream by posted date, last commented or by date unthreaded + * * Requires: network + */ + class Activity_order { function widget($arr) { @@ -22,7 +28,7 @@ class Activity_order { switch($_GET['order']){ case 'post': $postord_active = 'active'; - set_pconfig(local_channel(), 'mod_network', 'order', 1); + set_pconfig(local_channel(), 'mod_network', 'order', 1); break; case 'comment': $commentord_active = 'active'; diff --git a/Zotlabs/Widget/Admin.php b/Zotlabs/Widget/Admin.php index f349377a0..0a7a6925f 100644 --- a/Zotlabs/Widget/Admin.php +++ b/Zotlabs/Widget/Admin.php @@ -1,5 +1,10 @@ <?php +/** + * * Name: Admin menu + * * Requires: admin + */ + namespace Zotlabs\Widget; class Admin { diff --git a/Zotlabs/Widget/Affinity.php b/Zotlabs/Widget/Affinity.php index 572af0503..e083e576c 100644 --- a/Zotlabs/Widget/Affinity.php +++ b/Zotlabs/Widget/Affinity.php @@ -1,5 +1,11 @@ <?php +/** + * * Name: Affinity Tool + * * Description: Filter the network stream by affinity, requires the Affinity Tool App + * * Requires: network + */ + namespace Zotlabs\Widget; use Zotlabs\Lib\Apps; @@ -13,7 +19,7 @@ class Affinity { if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) return; - + $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); @@ -54,7 +60,7 @@ class Affinity { '$refresh' => t('Refresh'), '$labels' => $label_str, )); - + $arr = array('html' => $x); call_hooks('main_slider',$arr); @@ -63,4 +69,4 @@ class Affinity { } } - + diff --git a/Zotlabs/Widget/Filer.php b/Zotlabs/Widget/Filer.php index 5d6f96a87..bac1c339e 100644 --- a/Zotlabs/Widget/Filer.php +++ b/Zotlabs/Widget/Filer.php @@ -10,7 +10,6 @@ class Filer { if(! local_channel()) return ''; - $selected = ((x($_REQUEST,'file')) ? $_REQUEST['file'] : ''); $terms = array(); diff --git a/Zotlabs/Widget/Hq_controls.php b/Zotlabs/Widget/Hq_controls.php index 91335fd76..7b1fe817d 100644 --- a/Zotlabs/Widget/Hq_controls.php +++ b/Zotlabs/Widget/Hq_controls.php @@ -1,5 +1,12 @@ <?php +/** + * * Name: HQ Controls + * * Description: Control buttons for the HQ module + * * Author: Mario Vavti + * * Requires: hq + */ + namespace Zotlabs\Widget; use Zotlabs\Lib\Apps; diff --git a/Zotlabs/Widget/Messages.php b/Zotlabs/Widget/Messages.php index 86ce2fa29..d045ae85b 100644 --- a/Zotlabs/Widget/Messages.php +++ b/Zotlabs/Widget/Messages.php @@ -1,5 +1,13 @@ <?php +/** + * * Name: HQ Messages + * * Description: Quick access to messages, direct messages, starred messages (if enabled) and notifications + * * Author: Mario Vavti + * * Requires: hq + */ + + namespace Zotlabs\Widget; use App; diff --git a/Zotlabs/Widget/Notes.php b/Zotlabs/Widget/Notes.php index 659b62390..2e8e04e93 100644 --- a/Zotlabs/Widget/Notes.php +++ b/Zotlabs/Widget/Notes.php @@ -1,5 +1,13 @@ <?php +/** + * * Name: Notes + * * Description: A simple notes widget, requires the Notes App + * * Author: Mike Macgirvin + * * Author: Mario Vavti + * * Maintainer: Mario Vavti + */ + namespace Zotlabs\Widget; use Zotlabs\Lib\Apps; @@ -33,9 +41,6 @@ class Notes { ] )); - - - return $o; } } diff --git a/Zotlabs/Widget/Notifications.php b/Zotlabs/Widget/Notifications.php index a818ae40a..0e02d5cc1 100644 --- a/Zotlabs/Widget/Notifications.php +++ b/Zotlabs/Widget/Notifications.php @@ -1,5 +1,11 @@ <?php +/** + * * Name: Notifications + * * Description: Shows all kind of notifications + * * Author: Mario Vavti + */ + namespace Zotlabs\Widget; class Notifications { diff --git a/app/pdledit.apd b/app/pdledit_gui.apd index e13c04cb5..85a95a067 100644 --- a/app/pdledit.apd +++ b/app/pdledit_gui.apd @@ -1,5 +1,5 @@ -version: 3 -url: $baseurl/pdledit +version: 1 +url: $baseurl/pdledit_gui requires: local_channel name: PDL Editor photo: icon:object-group diff --git a/include/plugin.php b/include/plugin.php index 95c9882d0..25fa22e10 100644 --- a/include/plugin.php +++ b/include/plugin.php @@ -794,6 +794,79 @@ function get_theme_info($theme){ } /** + * @brief Parse template comment in search of template info. + * + * like + * \code + * * Name: MyWidget + * * Description: A widget + * * Version: 1.2.3 + * * Author: John <profile url> + * * Author: Jane <email> + * * ContentRegionID: some_id + * * ContentRegionID: some_other_id + * * + *\endcode + * @param string $widget the name of the widget + * @return array with the information + */ +function get_template_info($template){ + $m = array(); + $info = array( + 'name' => $template, + 'description' => '', + 'author' => array(), + 'maintainer' => array(), + 'version' => '', + 'content_regions' => [] + ); + + $checkpaths = [ + "view/php/$template.php", + ]; + + $template_found = false; + + foreach ($checkpaths as $path) { + if (is_file($path)) { + $template_found = true; + $f = file_get_contents($path); + break; + } + } + + if(! ($template_found && $f)) + return $info; + + $f = escape_tags($f); + $r = preg_match("|/\*.*\*/|msU", $f, $m); + + if ($r) { + $ll = explode("\n", $m[0]); + foreach( $ll as $l ) { + $l = trim($l, "\t\n\r */"); + if ($l != ""){ + list($k, $v) = array_map("trim", explode(":", $l, 2)); + $k = strtolower($k); + if ($k == 'author' || $k == 'maintainer'){ + $r = preg_match("|([^<]+)<([^>]+)>|", $v, $m); + if ($r) { + $info[$k][] = array('name' => $m[1], 'link' => $m[2]); + } else { + $info[$k][] = array('name' => $v); + } + } + else { + $info[$k] = $v; + } + } + } + } + + return $info; +} + +/** * @brief Returns the theme's screenshot. * * The screenshot is expected as view/theme/$theme/img/screenshot.[png|jpg]. diff --git a/view/css/full.css b/view/css/full.css index 3f9a01d24..1a36c9b08 100644 --- a/view/css/full.css +++ b/view/css/full.css @@ -7,7 +7,6 @@ header #banner { } section { - display: block; - min-height: 112px; - margin: 50px 10px; + height: 100vh; + padding: 4.5rem 7px 200px 7px; } diff --git a/view/css/minimal.css b/view/css/minimal.css new file mode 100644 index 000000000..3bd04a5df --- /dev/null +++ b/view/css/minimal.css @@ -0,0 +1,3 @@ +section { + height: 100vh; +} diff --git a/view/css/mod_pdledit_gui.css b/view/css/mod_pdledit_gui.css new file mode 100644 index 000000000..dcda842dc --- /dev/null +++ b/view/css/mod_pdledit_gui.css @@ -0,0 +1,3 @@ +.aside_wrapper { + min-height: 70vh; +} diff --git a/view/pdl/mod_pdledit_gui.pdl b/view/pdl/mod_pdledit_gui.pdl new file mode 100644 index 000000000..7590b7fa0 --- /dev/null +++ b/view/pdl/mod_pdledit_gui.pdl @@ -0,0 +1 @@ +[template]none[/template] diff --git a/view/php/default.php b/view/php/default.php index 64d58a0c1..10e150906 100644 --- a/view/php/default.php +++ b/view/php/default.php @@ -1,3 +1,15 @@ +<?php +/** + * * Name: default + * * Description: Hubzilla default 3-column layout + * * Version: 1 + * * Author: Mario Vavti + * * Maintainer: Mario Vavti + * * ContentRegion: aside, left_aside_wrapper + * * ContentRegion: content, region_2 + * * ContentRegion: right_aside, right_aside_wrapper + */ +?> <!DOCTYPE html > <html prefix="og: http://ogp.me/ns#"> <head> @@ -8,7 +20,7 @@ <body <?php if($page['direction']) echo 'dir="rtl"' ?> > <?php if(x($page,'banner')) echo $page['banner']; ?> <header><?php if(x($page,'header')) echo $page['header']; ?></header> - <?php if(x($page,'nav')) echo $page['nav']; ?></nav> + <?php if(x($page,'nav')) echo $page['nav']; ?> <main> <div class="content"> <div class="columns"> diff --git a/view/php/full.php b/view/php/full.php index 1a9cd9a84..d855fb650 100644 --- a/view/php/full.php +++ b/view/php/full.php @@ -1,3 +1,13 @@ +<?php +/** + * * Name: full + * * Description: A single column full width layout with the hubzilla navbar + * * Version: 1 + * * Author: None + * * Maintainer: None + * * ContentRegion: content, region_1 + */ +?> <!DOCTYPE html > <html prefix="og: http://ogp.me/ns#"> <head> @@ -8,8 +18,9 @@ <body <?php if($page['direction']) echo 'dir="rtl"' ?> > <?php if(x($page,'banner')) echo $page['banner']; ?> <header><?php if(x($page,'header')) echo $page['header']; ?></header> - <nav class="navbar fixed-top navbar-expand-lg navbar-dark bg-dark"><?php if(x($page,'nav')) echo $page['nav']; ?></nav> - <section id="region_2"><?php if(x($page,'content')) echo $page['content']; ?> + <?php if(x($page,'nav')) echo $page['nav']; ?> + <section id="region_1"> + <?php if(x($page,'content')) echo $page['content']; ?> <div id="page-footer"></div> </section> </body> diff --git a/view/php/minimal.php b/view/php/minimal.php index 3572f3b5c..3fab0c5f9 100644 --- a/view/php/minimal.php +++ b/view/php/minimal.php @@ -1,14 +1,25 @@ -<!DOCTYPE html >
-<html prefix="og: http://ogp.me/ns#">
-<head>
- <title><?php if(x($page,'title')) echo $page['title'] ?></title>
- <script>var baseurl="<?php echo z_root() ?>";</script>
- <?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?>
-</head>
-<body>
- <section style="margin:0px!important; padding:0px!important; float:none!important;display:block!important;"><?php if(x($page,'content')) echo $page['content']; ?>
- <div id="page-footer"></div>
- </section>
-</body>
-</html>
-
+<?php +/** + * * Name: full + * * Description: A single column full width layout without a navbar + * * Version: 1 + * * Author: None + * * Maintainer: None + * * ContentRegion: content, region_1 + */ +?> +<!DOCTYPE html > +<html prefix="og: http://ogp.me/ns#"> +<head> + <title><?php if(x($page,'title')) echo $page['title'] ?></title> + <script>var baseurl="<?php echo z_root() ?>";</script> + <?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?> +</head> +<body> + <section id="region_1"> + <?php if(x($page,'content')) echo $page['content']; ?> + <div id="page-footer"></div> + </section> +</body> +</html> + diff --git a/view/php/none.php b/view/php/none.php index 51d0e83dc..5e92310b7 100644 --- a/view/php/none.php +++ b/view/php/none.php @@ -1 +1,10 @@ +<?php +/** + * * Name: none + * * Description: A barebones empty single page template + * * Version: 1 + * * Author: None + * * Maintainer: None + */ +?> <?php if(x($page,'content')) echo $page['content']; ?> diff --git a/view/php/zen.php b/view/php/zen.php index a96cf722f..24aae951b 100644 --- a/view/php/zen.php +++ b/view/php/zen.php @@ -1,11 +1,11 @@ <!DOCTYPE html> <html prefix="og: http://ogp.me/ns#"> -<head> - <title><?php if(x($page,'title')) echo $page['title'] ?></title> - <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1"> - <?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?> -</head> -<body> - <?php if(x($page,'content')) echo $page['content']; ?> -</body> + <head> + <title><?php if(x($page,'title')) echo $page['title'] ?></title> + <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1"> + <?php if(x($page,'htmlhead')) echo $page['htmlhead'] ?> + </head> + <body> + <?php if(x($page,'content')) echo $page['content']; ?> + </body> </html> diff --git a/view/theme/redbasic/js/redbasic.js b/view/theme/redbasic/js/redbasic.js index c0c03d4f8..04469cb85 100644 --- a/view/theme/redbasic/js/redbasic.js +++ b/view/theme/redbasic/js/redbasic.js @@ -17,8 +17,13 @@ $(document).ready(function() { } $('#css3-calc').remove(); // Remove the test element - stickyScroll('.aside_spacer_left', '.aside_spacer_top_left', '.content', parseFloat(window.getComputedStyle(document.querySelector('#region_1')).getPropertyValue('padding-top')), 0); - stickyScroll('.aside_spacer_right', '.aside_spacer_top_right', '.content', parseFloat(window.getComputedStyle(document.querySelector('#region_3')).getPropertyValue('padding-top')), 20); + if (document.querySelector('#region_1')) { + stickyScroll('.aside_spacer_left', '.aside_spacer_top_left', '.content', parseFloat(window.getComputedStyle(document.querySelector('#region_1')).getPropertyValue('padding-top')), 0); + } + + if (document.querySelector('#region_3')) { + stickyScroll('.aside_spacer_right', '.aside_spacer_top_right', '.content', parseFloat(window.getComputedStyle(document.querySelector('#region_3')).getPropertyValue('padding-top')), 20); + } $('#expand-aside').on('click', function() { if($('main').hasClass('region_1-on')){ @@ -121,8 +126,14 @@ function setStyle(element, cssProperty) { } function stickyScroll(sticky, stickyTop, container, topOffset, bottomOffset) { + var lastScrollTop = 0; var sticky = document.querySelector(sticky); + + if (!sticky) { + return; + } + var stickyHeight = sticky.getBoundingClientRect().height; var stickyTop = document.querySelector(stickyTop); var content = document.querySelector(container); diff --git a/view/tpl/pdledit_gui.tpl b/view/tpl/pdledit_gui.tpl new file mode 100644 index 000000000..8c4ca3e50 --- /dev/null +++ b/view/tpl/pdledit_gui.tpl @@ -0,0 +1,243 @@ +<div id="pdledit_gui_offcanvas" class="offcanvas offcanvas-lg offcanvas-bottom shadow border rounded-top start-50 translate-middle-x" tabindex="-1" data-bs-backdrop="false" data-bs-scroll="true" style="min-width: 300px"> + <div id="pdledit_gui_offcanvas_body" class="offcanvas-body"></div> + <div class="offcanvas-header"> + <div class="offcanvas-title h3"></div> + <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button> + </div> +</div> + +<div id="pdledit_gui_offcanvas_edit" class="offcanvas offcanvas-lg offcanvas-bottom shadow border rounded-top start-50 translate-middle-x" tabindex="-1" data-bs-backdrop="false" data-bs-scroll="true" style="min-width: 300px"> + <div id="pdledit_gui_offcanvas_edit_body" class="offcanvas-body"> + <textarea id="pdledit_gui_offcanvas_edit_textarea" class="form-control font-monospace h-100"></textarea> + </div> + <div class="offcanvas-header"> + <button id="pdledit_gui_offcanvas_edit_submit" type="button" class="btn btn-primary">Submit</button> + <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button> + </div> +</div> + +<div id="pdledit_gui_offcanvas_submit" class="offcanvas offcanvas-lg offcanvas-bottom shadow border rounded-top start-50 translate-middle-x" tabindex="-1" data-bs-backdrop="false" data-bs-scroll="true" style="min-width: 300px"> + <div id="pdledit_gui_offcanvas_submit_body" class="offcanvas-body"></div> + <div class="offcanvas-header"> + <button id="pdledit_gui_offcanvas_submit_submit" type="button" class="btn btn-primary">Submit</button> + <button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button> + </div> +</div> + +<ul class="nav position-fixed bottom-0 start-50 bg-light translate-middle-x text-uppercase" style="min-width: 300px"> + <li class="nav-item"> + <a id="pdledit_gui_modules" class="nav-link" href="#">Modules</a> + </li> + <li class="nav-item"> + <a id="pdledit_gui_templates" class="nav-link" href="#">Templates</a> + </li> + <li class="nav-item"> + <a id="pdledit_gui_items" class="nav-link" href="#">Items</a> + </li> + <li class="nav-item"> + <a id="pdledit_gui_src" class="nav-link" href="#">Source</a> + </li> + {{if $module_modified}} + <li class="nav-item"> + <a id="pdledit_gui_reset" class="nav-link" href="#">Reset</a> + </li> + {{/if}} + <li class="nav-item"> + <a id="pdledit_gui_save" class="nav-link" href="#">Apply</a> + </li> +</ul> + +<script> + $(document).ready(function() { + let poi; + let regions = []; + let content_regions = []; + let page_src = atob('{{$page_src}}'); + + let offcanvas = new bootstrap.Offcanvas(document.getElementById('pdledit_gui_offcanvas')); + let edit_offcanvas = new bootstrap.Offcanvas(document.getElementById('pdledit_gui_offcanvas_edit')); + let submit_offcanvas = new bootstrap.Offcanvas(document.getElementById('pdledit_gui_offcanvas_submit')); + + {{foreach $content_regions as $content_region}} + regions.push('{{$content_region.0}}'); + content_regions.push('{{$content_region.1}}'); + + let sortable_{{$content_region.1}} = document.getElementById('{{$content_region.1}}'); + new Sortable(sortable_{{$content_region.1}}, { + group: 'shared', + handle: '.pdledit_gui_item_handle', + animation: 150 + }); + {{/foreach}} + + let sortable_items = document.getElementById('pdledit_gui_offcanvas_body'); + new Sortable(sortable_items, { + group: { + name: 'shared', + pull: 'clone', + put: false + }, + sort: false, + handle: '.pdledit_gui_item_handle', + animation: 150, + onEnd: function (e) { + $(e.item).find('button').removeClass('disabled'); + } + }); + + $(document).on('click', '.pdledit_gui_item_src', function(e) { + poi = this.closest('.pdledit_gui_item'); + let src = atob(poi.dataset.src); + $('#pdledit_gui_offcanvas_edit_textarea').val(src); + $('#pdledit_gui_offcanvas_edit_textarea').bbco_autocomplete('comanche'); + edit_offcanvas.show(); + }); + + $(document).on('click', '.pdledit_gui_item_remove', function(e) { + poi = this.closest('.pdledit_gui_item'); + $(poi).remove(); + }); + + $(document).on('click', '#pdledit_gui_offcanvas_edit_submit', function(e) { + let src = $('#pdledit_gui_offcanvas_edit_textarea').val(); + + if (poi) { + poi.dataset.src = btoa(src); + } + else { + $.post( + 'pdledit_gui', + { + 'save_src': 1, + 'module': '{{$module}}', + 'src': $('#pdledit_gui_offcanvas_edit_textarea').val() + } + ) + .done(function(data) { + if (data.success) { + window.location.href = 'pdledit_gui/' + data.module; + } + }); + } + + edit_offcanvas.hide(); + }); + + $(document).on('click', '#pdledit_gui_offcanvas_submit_submit', function(e) { + if ($('#pdledit_gui_templates_form').length) { + $.post( + 'pdledit_gui', + { + 'save_template': 1, + 'module': '{{$module}}', + 'data': $('#pdledit_gui_templates_form').serializeArray() + } + ) + .done(function(data) { + if (data.success) { + window.location.href = 'pdledit_gui/' + data.module; + } + }); + } + + submit_offcanvas.hide(); + }); + + $(document).on('click', '#pdledit_gui_src', function(e) { + e.preventDefault(); + poi = null; // this is important! + + let obj = {}; + + content_regions.forEach(function (content_region, i) { + let data_src = []; + $('#' + content_region + ' > .card').each(function () { + data_src.push(atob(this.dataset.src)); + }); + obj[regions[i]] = data_src; + }); + + for (let [region, entries] of Object.entries(obj)) { + let region_pdl = ''; + + entries.forEach(function (entry) { + region_pdl = region_pdl.concat(entry + "\r\n"); + }); + + let regex_str = '\\[region=' + region + '\\](.*?)\\[\\/region\\]'; + let replace_str = '[region=' + region + ']' + "\r\n" + region_pdl + "\r\n" + '[/region]' + let regex = new RegExp(regex_str, 'ism'); + + page_src = page_src.replace(regex, replace_str); + } + + $('#pdledit_gui_offcanvas_edit_textarea').val(page_src); + $('#pdledit_gui_offcanvas_edit_textarea').bbco_autocomplete('comanche'); + edit_offcanvas.show(); + }); + + $(document).on('click', '#pdledit_gui_items', function(e) { + e.preventDefault(); + $('#pdledit_gui_offcanvas_body').html(atob('{{$items}}')); + offcanvas.show(); + }); + + $(document).on('click', '#pdledit_gui_templates', function(e) { + e.preventDefault(); + $('#pdledit_gui_offcanvas_submit_body').html(atob('{{$templates}}')); + + submit_offcanvas.show(); + }); + + $(document).on('click', '#pdledit_gui_modules', function(e) { + e.preventDefault(); + $('#pdledit_gui_offcanvas_body').html(atob('{{$modules}}')); + offcanvas.show(); + }); + + $(document).on('click', '#pdledit_gui_save', function(e) { + e.preventDefault(); + + let obj = {}; + + content_regions.forEach(function (content_region, i) { + let data_src = []; + $('#' + content_region + ' > .card').each(function () { + data_src.push(this.dataset.src); + }); + obj[regions[i]] = data_src; + }); + + $.post( + 'pdledit_gui', + { + 'save': 1, + 'module': '{{$module}}', + 'data': JSON.stringify(obj) + } + ) + .done(function(data) { + if (data.success) { + window.location.href = 'pdledit_gui/' + data.module; + } + }); + }); + + $(document).on('click', '#pdledit_gui_reset', function(e) { + e.preventDefault(); + $.post( + 'pdledit_gui', + { + 'reset': 1, + 'module': '{{$module}}' + } + ) + .done(function(data) { + if (data.success) { + window.location.href = 'pdledit_gui/' + data.module; + } + }); + }); + + }); +</script> diff --git a/view/tpl/pdledit_gui_item.tpl b/view/tpl/pdledit_gui_item.tpl new file mode 100644 index 000000000..9d0095ed4 --- /dev/null +++ b/view/tpl/pdledit_gui_item.tpl @@ -0,0 +1,18 @@ +<div class="pdledit_gui_item card mb-3" data-src="{{$entry.src}}"> + <div class="card-header d-flex justify-content-between"> + <span class="text-uppercase">{{$entry.name}}</span> + <div class="badge rounded-pill{{if $entry.type === 'widget'}} bg-info text-dark{{/if}}{{if $entry.type === 'content'}} bg-primary{{/if}}{{if $entry.type === 'menu'}} bg-secondary{{/if}}{{if $entry.type === 'block'}} bg-warning text-dark{{/if}}"> + {{$entry.type}} + </div> + </div> + <div class="card-body"> + {{if $entry.desc}} + <div class="mb-3 text-muted">{{$entry.desc}}</div> + {{/if}} + {{if $entry.type !== 'content'}} + <button type="button" class="btn btn-sm btn-outline-primary pdledit_gui_item_src{{if $disable_controls}} disabled{{/if}}">Edit</button> + <button type="button" class="btn btn-sm btn-outline-danger pdledit_gui_item_remove{{if $disable_controls}} disabled{{/if}}">Remove</button> + <i class="fa fa-fw fa-arrows-alt m-2 float-end cursor-pointer pdledit_gui_item_handle"></i> + {{/if}} + </div> +</div> diff --git a/view/tpl/pdledit_gui_templates.tpl b/view/tpl/pdledit_gui_templates.tpl new file mode 100644 index 000000000..0f3d8a7b6 --- /dev/null +++ b/view/tpl/pdledit_gui_templates.tpl @@ -0,0 +1,11 @@ +<form id="pdledit_gui_templates_form"> + {{foreach $templates as $template}} + <div class="form-check mb-2"> + <input class="form-check-input" type="radio" name="template" id="id_template_{{$template.name}}" value="{{$template.name}}" {{if $template.name == $active}} checked{{/if}}> + <label class="form-check-label" for="id_template_{{$template.name}}"> + {{$template.name}} + </label> + <small class="text-muted">{{$template.desc}}</small> + </div> + {{/foreach}} +</form> |