Overview

Packages

  • Auth
  • Core
  • Horde
    • Imsp
  • None
  • Notification

Classes

  • Horde
  • Horde_Config
  • Horde_Config_Form
  • Horde_Core_ActiveSync_Connector
  • Horde_Core_ActiveSync_Driver
  • Horde_Core_Ajax_Application
  • Horde_Core_Ajax_Imple
  • Horde_Core_Ajax_Imple_AutoCompleter
  • Horde_Core_Ajax_Imple_Geocoder_Geonames
  • Horde_Core_Ajax_Imple_SpellChecker
  • Horde_Core_Alarm_Handler_Notify
  • Horde_Core_Auth_Application
  • Horde_Core_Auth_Composite
  • Horde_Core_Auth_Ldap
  • Horde_Core_Auth_Msad
  • Horde_Core_Auth_Shibboleth
  • Horde_Core_Auth_Signup_Base
  • Horde_Core_Auth_Signup_Form
  • Horde_Core_Auth_Signup_Null
  • Horde_Core_Auth_Signup_Sql
  • Horde_Core_Auth_Signup_SqlObject
  • Horde_Core_Autoloader_Callback_Mime
  • Horde_Core_Autoloader_Callback_Nls
  • Horde_Core_Block
  • Horde_Core_Block_Collection
  • Horde_Core_Block_Layout
  • Horde_Core_Block_Layout_Manager
  • Horde_Core_Block_Layout_View
  • Horde_Core_Block_Upgrade
  • Horde_Core_Browser
  • Horde_Core_Bundle
  • Horde_Core_Cli
  • Horde_Core_Controller_NotFound
  • Horde_Core_Controller_RequestConfiguration
  • Horde_Core_Controller_RequestMapper
  • Horde_Core_Controller_SettingsFinder
  • Horde_Core_Db_Migration
  • Horde_Core_Factory_ActiveSyncBackend
  • Horde_Core_Factory_ActiveSyncServer
  • Horde_Core_Factory_ActiveSyncState
  • Horde_Core_Factory_Ajax
  • Horde_Core_Factory_Alarm
  • Horde_Core_Factory_Auth
  • Horde_Core_Factory_AuthSignup
  • Horde_Core_Factory_Base
  • Horde_Core_Factory_BlockCollection
  • Horde_Core_Factory_Browser
  • Horde_Core_Factory_Cache
  • Horde_Core_Factory_Crypt
  • Horde_Core_Factory_Data
  • Horde_Core_Factory_Db
  • Horde_Core_Factory_DbBase
  • Horde_Core_Factory_DbPear
  • Horde_Core_Factory_Dns
  • Horde_Core_Factory_Editor
  • Horde_Core_Factory_Facebook
  • Horde_Core_Factory_Group
  • Horde_Core_Factory_History
  • Horde_Core_Factory_HttpClient
  • Horde_Core_Factory_Identity
  • Horde_Core_Factory_Image
  • Horde_Core_Factory_Imple
  • Horde_Core_Factory_Imsp
  • Horde_Core_Factory_ImspAuth
  • Horde_Core_Factory_Injector
  • Horde_Core_Factory_KolabServer
  • Horde_Core_Factory_KolabSession
  • Horde_Core_Factory_KolabStorage
  • Horde_Core_Factory_Ldap
  • Horde_Core_Factory_Lock
  • Horde_Core_Factory_Logger
  • Horde_Core_Factory_LoginTasks
  • Horde_Core_Factory_Mail
  • Horde_Core_Factory_Mapper
  • Horde_Core_Factory_Matcher
  • Horde_Core_Factory_Memcache
  • Horde_Core_Factory_MimeViewer
  • Horde_Core_Factory_Notification
  • Horde_Core_Factory_Perms
  • Horde_Core_Factory_PermsCore
  • Horde_Core_Factory_Prefs
  • Horde_Core_Factory_Request
  • Horde_Core_Factory_Secret
  • Horde_Core_Factory_SessionHandler
  • Horde_Core_Factory_Share
  • Horde_Core_Factory_ShareBase
  • Horde_Core_Factory_Template
  • Horde_Core_Factory_TextFilter
  • Horde_Core_Factory_ThemesCache
  • Horde_Core_Factory_Token
  • Horde_Core_Factory_Tree
  • Horde_Core_Factory_Twitter
  • Horde_Core_Factory_UrlShortener
  • Horde_Core_Factory_Vfs
  • Horde_Core_Factory_View
  • Horde_Core_Factory_Weather
  • Horde_Core_Group_Ldap
  • Horde_Core_Log_Logger
  • Horde_Core_LoginTasks
  • Horde_Core_LoginTasks_Backend_Horde
  • Horde_Core_LoginTasks_SystemTask_Upgrade
  • Horde_Core_Mime_Viewer_Syntaxhighlighter
  • Horde_Core_Mime_Viewer_Vcard
  • Horde_Core_Notification_Event_Status
  • Horde_Core_Notification_Handler_Decorator_Hordelog
  • Horde_Core_Notification_Storage_Session
  • Horde_Core_Perms
  • Horde_Core_Perms_Ui
  • Horde_Core_Prefs_Cache_Session
  • Horde_Core_Prefs_Identity
  • Horde_Core_Prefs_Storage_Configuration
  • Horde_Core_Prefs_Storage_Hooks
  • Horde_Core_Prefs_Storage_Upgrade
  • Horde_Core_Prefs_Ui
  • Horde_Core_Prefs_Ui_Widgets
  • Horde_Core_Share_Driver
  • Horde_Core_Share_FactoryCallback
  • Horde_Core_Sidebar
  • Horde_Core_Text_Filter_Bbcode
  • Horde_Core_Text_Filter_Emails
  • Horde_Core_Text_Filter_Emoticons
  • Horde_Core_Text_Filter_Highlightquotes
  • Horde_Core_Translation
  • Horde_Core_Tree_Html
  • Horde_Core_Tree_Javascript
  • Horde_Core_Tree_Simplehtml
  • Horde_Core_Ui_FlagImage
  • Horde_Core_Ui_JsCalendar
  • Horde_Core_Ui_Language
  • Horde_Core_Ui_Layout
  • Horde_Core_Ui_ModalFormRenderer
  • Horde_Core_Ui_Pager
  • Horde_Core_Ui_Tabs
  • Horde_Core_Ui_TagCloud
  • Horde_Core_Ui_VarRenderer
  • Horde_Core_Ui_VarRenderer_Html
  • Horde_Core_Ui_VarRenderer_TablesetHtml
  • Horde_Core_Ui_Widget
  • Horde_ErrorHandler
  • Horde_Help
  • Horde_Menu
  • Horde_Registry
  • Horde_Registry_Api
  • Horde_Registry_Application
  • Horde_Registry_Caller
  • Horde_Registry_Nlsconfig
  • Horde_Script_Files
  • Horde_Session
  • Horde_Session_Null
  • Horde_Themes
  • Horde_Themes_Cache
  • Horde_Themes_Css
  • Horde_Themes_Element
  • Horde_Themes_Image
  • Horde_Themes_Sound

Exceptions

  • Horde_Exception_HookNotSet
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * This class provides an interface to handling CSS stylesheets for Horde
  4:  * applications.
  5:  *
  6:  * Copyright 2010-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file COPYING for license information (LGPL). If you
  9:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 10:  *
 11:  * @author   Michael Slusarz <slusarz@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 14:  * @package  Core
 15:  */
 16: class Horde_Themes_Css
 17: {
 18:     /**
 19:      * The theme cache ID.
 20:      *
 21:      * @var string
 22:      */
 23:     protected $_cacheid;
 24: 
 25:     /**
 26:      * A list of additional stylesheet files to add to the output.
 27:      *
 28:      * @var array
 29:      */
 30:     protected $_cssFiles = array();
 31: 
 32:     /**
 33:      * A list of additional themed stylesheet files to add to the output.
 34:      *
 35:      * @var array
 36:      */
 37:     protected $_cssThemeFiles = array();
 38: 
 39:     /**
 40:      * Adds an external stylesheet to the output.
 41:      *
 42:      * @param string $file  The CSS filepath.
 43:      * @param string $url   The CSS URL.
 44:      */
 45:     public function addStylesheet($file, $url)
 46:     {
 47:         $this->_cssFiles[$file] = $url;
 48:     }
 49: 
 50:     /**
 51:      * Adds a themed stylesheet to the output.
 52:      *
 53:      * @param string $file  The stylesheet name.
 54:      */
 55:     public function addThemeStylesheet($file)
 56:     {
 57:         $this->_cssThemeFiles[$file] = true;
 58:     }
 59: 
 60:     /**
 61:      * Generate the stylesheet URLs needed to display the current page.
 62:      * Honors configuration choices as to stylesheet caching.
 63:      *
 64:      * @param array $opts  Additional options:
 65:      * <pre>
 66:      * 'app' - (string) The current application.
 67:      * 'nobase' - (boolean) If true, don't load base stylesheets.
 68:      * 'nohorde' - (boolean) If true, don't load files from Horde.
 69:      * 'nocache' - (boolean) If true, don't load files from Cache.
 70:      *             @since Horde_Core 1.3.0
 71:      * 'sub' - (string) A subdirectory containing additional CSS files to
 72:      *         load as an overlay to the base CSS files.
 73:      * 'subonly' - (boolean) If true, only load the files in 'sub', not
 74:      *             the default theme files.
 75:      * 'theme' - (string) Use this theme instead of the default.
 76:      * 'themeonly' - (boolean) If true, only load the theme files.
 77:      * </pre>
 78:      *
 79:      * @return array  The list of URLs to display (Horde_Url objects).
 80:      */
 81:     public function getStylesheetUrls(array $opts = array())
 82:     {
 83:         global $conf, $injector, $prefs, $registry;
 84: 
 85:         $themesfs = $registry->get('themesfs');
 86:         $themesuri = $registry->get('themesuri');
 87: 
 88:         $theme = isset($opts['theme'])
 89:             ? $opts['theme']
 90:             : $prefs->getValue('theme');
 91:         $css = $this->getStylesheets($theme, $opts);
 92:         if (!count($css)) {
 93:             return array();
 94:         }
 95: 
 96:         $cache_type = !empty($opts['nocache']) || empty($conf['cachecss'])
 97:             ? 'none'
 98:             : $conf['cachecssparams']['driver'];
 99: 
100:         if ($cache_type == 'none') {
101:             $css_out = array();
102:             foreach ($css as $file) {
103:                 $css_out[] = Horde::url($file['uri'], true, array('append_session' => -1));
104:             }
105:             return $css_out;
106:         }
107: 
108:         $out = '';
109:         $sig = hash('md5', serialize($css) . $this->_cacheid);
110: 
111:         switch ($cache_type) {
112:         case 'filesystem':
113:             $css_filename = '/static/' . $sig . '.css';
114:             $css_path = $registry->get('fileroot', 'horde') . $css_filename;
115:             $css_url = Horde::url($registry->get('webroot', 'horde') . $css_filename, true, array('append_session' => -1));
116:             $exists = file_exists($css_path);
117:             break;
118: 
119:         case 'horde_cache':
120:             $cache = $injector->getInstance('Horde_Cache');
121: 
122:             // Do lifetime checking here, not on cache display page.
123:             $exists = $cache->exists($sig, empty($conf['cachecssparams']['lifetime']) ? 0 : $conf['cachecssparams']['lifetime']);
124:             $css_url = Horde::getCacheUrl('css', array('cid' => $sig));
125:             break;
126:         }
127: 
128:         if (!$exists) {
129:             $out = $this->loadCssFiles($css);
130: 
131:             /* Use CSS tidy to clean up file. */
132:             if ($conf['cachecssparams']['compress'] == 'php') {
133:                 try {
134:                     $out = $injector->getInstance('Horde_Core_Factory_TextFilter')->filter($out, 'csstidy');
135:                 } catch (Horde_Exception $e) {}
136:             }
137: 
138:             switch ($cache_type) {
139:             case 'filesystem':
140:                 if (!file_put_contents($css_path, $out)) {
141:                     throw new Horde_Exception('Could not write cached CSS file to disk.');
142:                 }
143:                 break;
144: 
145:             case 'horde_cache':
146:                 $cache->set($sig, $out);
147:                 break;
148:             }
149:         }
150: 
151:         return array($css_url);
152:     }
153: 
154:     /**
155:      * Return the list of base stylesheets to display.
156:      *
157:      * @param mixed $theme  The theme to use; specify an empty value to
158:      *                      retrieve the theme from user preferences, and
159:      *                      false for no theme.
160:      * @param array $opts   Additional options:
161:      * <pre>
162:      * 'app' - (string) The current application.
163:      * 'nobase' - (boolean) If true, don't load base stylesheets.
164:      * 'nohorde' - (boolean) If true, don't load files from Horde.
165:      * 'sub' - (string) A subdirectory containing additional CSS files to
166:      *         load as an overlay to the base CSS files.
167:      * 'subonly' - (boolean) If true, only load the files in 'sub', not
168:      *             the default theme files.
169:      * 'themeonly' - (boolean) If true, only load the theme files.
170:      * </pre>
171:      *
172:      * @return array  An array of 2-element array arrays containing 2 keys:
173:      * <pre>
174:      * fs - (string) Filesystem location of stylesheet.
175:      * uri - (string) URI of stylesheet.
176:      * </pre>
177:      */
178:     public function getStylesheets($theme = '', array $opts = array())
179:     {
180:         if (($theme === '') && isset($GLOBALS['prefs'])) {
181:             $theme = $GLOBALS['prefs']->getValue('theme');
182:         }
183: 
184:         $add_css = $css_out = array();
185:         $css_list = empty($opts['nobase'])
186:             ? $this->getBaseStylesheetList()
187:             : array();
188: 
189:         $css_list = array_unique(array_merge($css_list, array_keys($this->_cssThemeFiles)));
190: 
191:         $curr_app = empty($opts['app'])
192:             ? $GLOBALS['registry']->getApp()
193:             : $opts['app'];
194:         $mask = empty($opts['nohorde'])
195:             ? 0
196:             : Horde_Themes_Cache::APP_DEFAULT | Horde_Themes_Cache::APP_THEME;
197:         $sub = empty($opts['sub'])
198:             ? null
199:             : $opts['sub'];
200: 
201:         $cache = $GLOBALS['injector']->getInstance('Horde_Core_Factory_ThemesCache')->create($curr_app, $theme);
202:         $this->_cacheid = $cache->getCacheId();
203: 
204:         foreach ($css_list as $css_name) {
205:             if (empty($opts['subonly'])) {
206:                 $css_out = array_merge($css_out, array_reverse($cache->getAll($css_name, $mask)));
207:             }
208: 
209:             if ($sub) {
210:                 $css_out = array_merge($css_out, array_reverse($cache->getAll($sub . '/' . $css_name, $mask)));
211:             }
212:         }
213: 
214:         /* Add additional stylesheets added by code. */
215:         foreach ($this->_cssFiles as $f => $u) {
216:             if (file_exists($f)) {
217:                 $add_css[$f] = $u;
218:             }
219:         }
220: 
221:         /* Add user-defined additional stylesheets. */
222:         try {
223:             $add_css = array_merge($add_css, Horde::callHook('cssfiles', array($theme), 'horde'));
224:         } catch (Horde_Exception_HookNotSet $e) {
225:         }
226: 
227:         if ($curr_app != 'horde') {
228:             try {
229:                 $add_css = array_merge($add_css, Horde::callHook('cssfiles', array($theme), $curr_app));
230:             } catch (Horde_Exception_HookNotSet $e) {
231:             }
232:         }
233: 
234:         foreach ($add_css as $f => $u) {
235:             $css_out[] = array(
236:                 'fs' => $f,
237:                 'uri' => $u
238:             );
239:         }
240: 
241:         return $css_out;
242:     }
243: 
244:     /**
245:      * Returns the list of base stylesheets, based on the current language
246:      * and browser settings.
247:      *
248:      * @return array  A list of base CSS files to load.
249:      */
250:     public function getBaseStylesheetList()
251:     {
252:         $css_list = array('screen.css');
253: 
254:         if ($GLOBALS['registry']->nlsconfig->curr_rtl) {
255:             $css_list[] = 'rtl.css';
256:         }
257: 
258:         /* Collect browser specific stylesheets if needed. */
259:         switch ($GLOBALS['browser']->getBrowser()) {
260:         case 'msie':
261:             $ie_major = $GLOBALS['browser']->getMajor();
262:             if ($ie_major == 8) {
263:                 $css_list[] = 'ie8.css';
264:             } elseif ($ie_major == 7) {
265:                 $css_list[] = 'ie7.css';
266:             } elseif ($ie_major < 7) {
267:                 $css_list[] = 'ie6_or_less.css';
268:             }
269:             break;
270: 
271:         case 'opera':
272:             $css_list[] = 'opera.css';
273:             break;
274: 
275:         case 'mozilla':
276:             $css_list[] = 'mozilla.css';
277:             break;
278: 
279:         case 'webkit':
280:             $css_list[] = 'webkit.css';
281:         }
282: 
283:         return $css_list;
284:     }
285: 
286:     /**
287:      * Loads CSS files, cleans up the input, and concatenates to a string.
288:      *
289:      * @param array $files  List of CSS files as returned from
290:      *                      getStylesheets().
291:      *
292:      * @return string  CSS data.
293:      */
294:     public function loadCssFiles($files)
295:     {
296:         $dataurl = $GLOBALS['browser']->hasFeature('dataurl');
297:         $out = '';
298: 
299:         foreach ($files as $file) {
300:             $path = substr($file['uri'], 0, strrpos($file['uri'], '/') + 1);
301: 
302:             // Fix relative URLs, convert graphics URLs to data URLs
303:             // (if possible), remove multiple whitespaces, and strip
304:             // comments.
305:             $tmp = preg_replace(array('/(url\(["\']?)([^\/])/i', '/\s+/', '/\/\*.*?\*\//'), array('$1' . $path . '$2', ' ', ''), implode('', file($file['fs'], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)));
306:             if ($dataurl) {
307:                 $tmp = preg_replace_callback('/(background(?:-image)?:[^;}]*(?:url\(["\']?))(.*?)((?:["\']?\)))/i', array($this, '_base64Callback'), $tmp);
308:             }
309: 
310:             /* Scan to grab any @import tags within the CSS file. */
311:             $tmp = preg_replace_callback('/@import\s+url\(["\']?(.*?)["\']?\)(?:\s*;\s*)*/i', array($this, '_importCallback'), $tmp);
312: 
313:             $out .= $tmp;
314:         }
315: 
316:         return $out;
317:     }
318: 
319:     /**
320:      * Callback for loadCssFiles() to convert images to base64 data
321:      * strings.
322:      *
323:      * @param array $matches  The list of matches from preg_replace_callback.
324:      *
325:      * @return string  The image string.
326:      */
327:     protected function _base64Callback($matches)
328:     {
329:         /* Limit data to 16 KB in stylesheets. */
330:         return $matches[1] . Horde::base64ImgData($matches[2], 16384) . $matches[3];
331:     }
332: 
333:     /**
334:      * Callback for loadCssFiles() to process import tags.
335:      *
336:      * @param array $matches  The list of matches from preg_replace_callback.
337:      *
338:      * @return string  CSS string.
339:      */
340:     protected function _importCallback($matches)
341:     {
342:         $ob = Horde_Themes_Element::fromUri($matches[1]);
343: 
344:         return $this->loadCssFiles(array(array(
345:             'fs' => $ob->fs,
346:             'uri' => $ob->uri
347:         )));
348:     }
349: 
350: }
351: 
API documentation generated by ApiGen