From 8b42f6aeb8a4fb3dacc7215f2f0488af63fd80ae Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Thu, 15 Oct 2015 17:28:56 +0000 Subject: [PATCH] Load all category CSS once when loading the application to avoid missing category colors. --- admin/inc/class.admin_categories.inc.php | 4 +- etemplate/inc/class.etemplate_new.inc.php | 3 + .../js/et2_extension_nextmatch_rowProvider.js | 50 +------------ etemplate/js/et2_widget_selectbox.js | 6 +- etemplate/templates/default/etemplate2.css | 5 +- phpgwapi/categories.php | 71 +++++++++++++++++++ phpgwapi/inc/class.egw_framework.inc.php | 17 +++-- 7 files changed, 99 insertions(+), 57 deletions(-) create mode 100644 phpgwapi/categories.php diff --git a/admin/inc/class.admin_categories.inc.php b/admin/inc/class.admin_categories.inc.php index 28f23caa62..db68d1f2a6 100644 --- a/admin/inc/class.admin_categories.inc.php +++ b/admin/inc/class.admin_categories.inc.php @@ -573,8 +573,8 @@ class admin_categories if($tmpl instanceof etemplate_widget_template) { - // Send categories for row colors - $sel_options['cat_id'] = etemplate_widget_menupopup::typeOptions('select-cat', ',,,'.$appname.',0'); + // Category styles + egw_framework::includeCSS('/phpgwapi/categories.php?app='.$appname); } $tmpl->exec($this->list_link,$content,$sel_options,$readonlys,array( diff --git a/etemplate/inc/class.etemplate_new.inc.php b/etemplate/inc/class.etemplate_new.inc.php index c5d1201016..86284bc642 100644 --- a/etemplate/inc/class.etemplate_new.inc.php +++ b/etemplate/inc/class.etemplate_new.inc.php @@ -221,6 +221,9 @@ class etemplate_new extends etemplate_widget_template { egw_framework::validate_file('.','app',$app,false); } + // Category styles + egw_framework::includeCSS('/phpgwapi/categories.php?app='.$app); + // set action attribute for autocomplete form tag // as firefox complains on about:balnk action, thus we have to literaly submit the form to a blank html $form_action = "about:blank"; diff --git a/etemplate/js/et2_extension_nextmatch_rowProvider.js b/etemplate/js/et2_extension_nextmatch_rowProvider.js index 8539fc0d60..46882991fc 100644 --- a/etemplate/js/et2_extension_nextmatch_rowProvider.js +++ b/etemplate/js/et2_extension_nextmatch_rowProvider.js @@ -515,58 +515,14 @@ var et2_nextmatch_rowProvider = ClassWithAttributes.extend( cats = classes.match(this.cat_regexp) || []; classes = classes.replace(this.cat_regexp, ''); - // Get category info - if(!this.categories) - { - // Nextmatch category filter should put them here - var categories = _mgrs["sel_options"].getEntry('cat_id'); - // Or they are in the global - 1 level up - if(!categories) categories = _mgrs["sel_options"].parentMgr.getEntry('cat_id'); - // If not using category (tracker, calendar list) look for sel_options in the rows - if(!categories) categories = _mgrs["sel_options"].parentMgr.getEntry(category_location); - if(!categories) categories = _mgrs["sel_options"].getEntry("${row}["+category_location + "]"); - - // Cache - if(categories) - { - if (!jQuery.isArray(categories)) - { - this.categories = categories; - } - else - { - this.categories = {}; - for(var i=0; i < categories.length; ++i) - { - var cat = categories[i]; - this.categories[cat.value] = cat; - } - } - } - } - for(var i = 0; i < cats.length && this.categories; i++) + // Set category class + for(var i = 0; i < cats.length; i++) { // Need cat_, classes can't start with a number var cat_id = cats[i].replace(this.cat_cleanup, ''); var cat_class = 'cat_'+cat_id; - // Check for existing class - // TODO - - var cat = this.categories[cat_id] || null; - for(var j = 0; cat == null && this.categories && j < this.categories.length; j++) - { - if(this.categories[j] && this.categories[j].value && this.categories[j].value == cat_id) - { - cat = this.categories[j]; - } - } - // Create class - if(cat && cat.color) - { - this._rootWidget.egw().css('.'+cat_class, "background-color: " + cat.color + ";"); - classes += ' '+cat_class; - } + classes += ' '+cat_class; } classes += " row_category"; } diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js index 3aca8020dd..4a3b7386f8 100644 --- a/etemplate/js/et2_widget_selectbox.js +++ b/etemplate/js/et2_widget_selectbox.js @@ -306,13 +306,15 @@ var et2_selectbox = et2_inputWidget.extend( if(option_data.icon) { var img = this.egw().image(option_data.icon); - jQuery(document.createElement("img")) + jQuery(document.createElement(img ? "img" : "div")) .attr("src", img) + .addClass('cat_icon cat_' + _value) .appendTo(label); } if(option_data.color) { - label.css("background-color",option_data.color); + label.css("background-color",option_data.color) + .addClass('cat_' + _value); } } label.append(jQuery(""+_label+"")); diff --git a/etemplate/templates/default/etemplate2.css b/etemplate/templates/default/etemplate2.css index b38ba1e0fb..e7c65694c2 100644 --- a/etemplate/templates/default/etemplate2.css +++ b/etemplate/templates/default/etemplate2.css @@ -368,9 +368,12 @@ button#cancel { .et2_selectbox input[type="checkbox"] { margin: 3px; } -.et2_selectbox .ui-multiselect-checkboxes img { +.et2_selectbox .ui-multiselect-checkboxes img, .et2_selectbox .ui-multiselect-checkboxes div { float: right; height: 1.8em; + width: 1.8em; + background-size: contain; + background-repeat: no-repeat; } .et2_selectbox .ui-multiselect-checkboxes div.ui-icon-close { visibility: hidden; diff --git a/phpgwapi/categories.php b/phpgwapi/categories.php new file mode 100644 index 0000000000..f53ef5da73 --- /dev/null +++ b/phpgwapi/categories.php @@ -0,0 +1,71 @@ + array( + 'currentapp' => 'preferences', + 'noheader' => true, + 'nocachecontrol' => true, + ) +); + +include '../header.inc.php'; + +// Get appname +$appname = $_GET['app'] && $GLOBALS['egw_info']['apps'][$_GET['app']] ? $_GET['app'] : categories::GLOBAL_APPNAME; + +$cats = new categories('', $appname); +$categories = $cats->return_array('all',0, false, '', 'ASC','',$appname==categories::GLOBAL_APPNAME); + +$content = "/* Category CSS for $appname */\n\n"; + +foreach($categories as $cat) +{ + if($cat['data']['color']) + { + $content .= ".cat_{$cat['id']} { background-color: {$cat['data']['color']};} /*{$cat['name']}*/\n"; + } + if($cat['data']['icon']) + { + $content .= ".cat_{$cat['id']} .cat_icon { background-image: url('". admin_categories::icon_url($cat['data']['icon']) ."');} /*{$cat['name']}*/\n"; + } +} + +// use an etag over categories +$etag = '"'.md5($content).'"'; + +// headers to allow caching, egw_framework specifies etag on url to force reload, even with Expires header +egw_session::cache_control(86400); // cache for 1 day +Header('Content-Type: text/css; charset=utf-8'); +Header('ETag: '.$etag); + +// if servers send a If-None-Match header, response with 304 Not Modified, if etag matches +if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag) +{ + header("HTTP/1.1 304 Not Modified"); + common::egw_exit(); +} + +// we run our own gzip compression, to set a correct Content-Length of the encoded content +if (in_array('gzip', explode(',',$_SERVER['HTTP_ACCEPT_ENCODING'])) && function_exists('gzencode')) +{ + $content = gzencode($content); + header('Content-Encoding: gzip'); +} + +// Content-Lenght header is important, otherwise browsers dont cache! +Header('Content-Length: '.bytes($content)); +echo $content; diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php index a46d3d7f2c..1671ac4621 100644 --- a/phpgwapi/inc/class.egw_framework.inc.php +++ b/phpgwapi/inc/class.egw_framework.inc.php @@ -1385,6 +1385,9 @@ abstract class egw_framework // eTemplate2 - load in top so sidebox has styles too self::includeCSS('/etemplate/templates/default/etemplate2.css'); + // Category styles + self::includeCSS('/phpgwapi/categories.php'); + // For mobile user-agent we prefer mobile theme over selected one with a final fallback to theme named as template $themes_to_check = array(); if (html::$ua_mobile) $themes_to_check[] = $this->template_dir.'/css/mobile.css'; @@ -1418,11 +1421,13 @@ abstract class egw_framework { foreach(self::resolve_css_includes($path) as $path) { - if (($mod = filemtime(EGW_SERVER_ROOT.$path)) > $max_modified) $max_modified = $mod; + list($file,$query) = explode('?',$path,2); + if (($mod = filemtime(EGW_SERVER_ROOT.$file)) > $max_modified) $max_modified = $mod; - if ($debug_minify || substr($path, -8) == '/app.css') // do NOT include app.css, as it changes from app to app + // do NOT include app.css or categories.php, as it changes from app to app + if ($debug_minify || substr($path, -8) == '/app.css' || substr($file,-14) == 'categories.php') { - $css_files .= ''."\n"; + $css_files .= ''."\n"; } else { @@ -1454,7 +1459,9 @@ abstract class egw_framework protected static function resolve_css_includes($path, &$pathes=array()) { $matches = null; - if (($to_check = file_get_contents (EGW_SERVER_ROOT.$path, false, null, -1, 1024)) && + + list($file,$query) = explode('?',$path,2); + if (($to_check = file_get_contents (EGW_SERVER_ROOT.$file, false, null, -1, 1024)) && stripos($to_check, '/*@import') !== false && preg_match_all('|/\*@import url\("([^"]+)"|i', $to_check, $matches)) { foreach($matches[1] as $import_path) @@ -2248,7 +2255,7 @@ abstract class egw_framework { $path = $app; } - if (!file_exists(EGW_SERVER_ROOT.$path)) + if (!file_exists(EGW_SERVER_ROOT.$path) && !file_exists(EGW_SERVER_ROOT . parse_url($path,PHP_URL_PATH))) { //error_log(__METHOD__."($app,$name) $path NOT found!"); return false;