openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit 8461a4f4a1d9bf0650f8c23590465c0e330f8773
parent 67d4a6c660b2b3848358805e088c529b77db9b4d
Author: Jan Dankert <devnull@localhost>
Date:   Sat, 16 Dec 2017 23:21:31 +0100

Eigenes Modul für alle Util-Klassen.

Diffstat:
dispatcher.php | 4++--
init.php | 6+-----
modules/util/.htaccess | 3+++
modules/util/AbstractTree.class.php | 172+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/AdministrationTree.class.php | 595+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Api.class.php | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/ArchiveTar.class.php | 398+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/ArchiveUnzip.class.php | 448+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/ArchiveZip.class.php | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Code.class.php | 45+++++++++++++++++++++++++++++++++++++++++++++
modules/util/Dynamic.class.php | 35+++++++++++++++++++++++++++++++++++
modules/util/FileUtils.class.php | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Ftp.class.php | 247+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/GlobalFunctions.class.php | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Html.class.php | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Http.class.php | 611+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/JSON.class.php | 809+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/JSqueeze.class.php | 1065+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Ldap.class.php | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Less.php | 10525+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Line.class.php | 153+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Macro.class.php | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Mail.class.php | 571+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/ProjectTree.class.php | 516+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Publish.class.php | 394+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Session.class.php | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Spyc.class.php | 1161+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Text.class.php | 393+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Transformer.class.php | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/TreeElement.class.php | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/Upload.class.php | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/XML.class.php | 170+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/config-default.php | 881+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
modules/util/exception/OpenRatException.class.php | 26++++++++++++++++++++++++++
modules/util/exception/SecurityException.class.php | 10++++++++++
modules/util/require.php | 33++++++++++++++++++++++++++++++---
util/.htaccess | 3---
util/AbstractTree.class.php | 172-------------------------------------------------------------------------------
util/AdministrationTree.class.php | 595-------------------------------------------------------------------------------
util/Api.class.php | 102-------------------------------------------------------------------------------
util/ArchiveTar.class.php | 398-------------------------------------------------------------------------------
util/ArchiveUnzip.class.php | 448-------------------------------------------------------------------------------
util/ArchiveZip.class.php | 90-------------------------------------------------------------------------------
util/Code.class.php | 45---------------------------------------------
util/Dynamic.class.php | 35-----------------------------------
util/FileUtils.class.php | 103-------------------------------------------------------------------------------
util/Ftp.class.php | 247-------------------------------------------------------------------------------
util/GlobalFunctions.class.php | 72------------------------------------------------------------------------
util/Html.class.php | 179-------------------------------------------------------------------------------
util/Http.class.php | 611-------------------------------------------------------------------------------
util/JSON.class.php | 809-------------------------------------------------------------------------------
util/JSqueeze.class.php | 1065-------------------------------------------------------------------------------
util/Ldap.class.php | 183-------------------------------------------------------------------------------
util/Less.php | 10525-------------------------------------------------------------------------------
util/Line.class.php | 153-------------------------------------------------------------------------------
util/Macro.class.php | 195-------------------------------------------------------------------------------
util/Mail.class.php | 571-------------------------------------------------------------------------------
util/ProjectTree.class.php | 516-------------------------------------------------------------------------------
util/Publish.class.php | 394-------------------------------------------------------------------------------
util/Session.class.php | 243-------------------------------------------------------------------------------
util/Spyc.class.php | 1161-------------------------------------------------------------------------------
util/Text.class.php | 393-------------------------------------------------------------------------------
util/Transformer.class.php | 103-------------------------------------------------------------------------------
util/TreeElement.class.php | 76----------------------------------------------------------------------------
util/Upload.class.php | 86-------------------------------------------------------------------------------
util/XML.class.php | 170-------------------------------------------------------------------------------
util/config-default.php | 881-------------------------------------------------------------------------------
util/exception/OpenRatException.class.php | 26--------------------------
util/exception/SecurityException.class.php | 10----------
util/include.inc.php | 66------------------------------------------------------------------
70 files changed, 20693 insertions(+), 20736 deletions(-)

diff --git a/dispatcher.php b/dispatcher.php @@ -37,7 +37,7 @@ try // Jetzt erst die Sitzung starten (nachdem alle Klassen zur Verfügung stehen). session_start(); - require_once( OR_SERVICECLASSES_DIR."Session.class.".PHP_EXT ); + require_once( OR_MODULES_DIR."util/Session.class.".PHP_EXT ); // Vorhandene Konfiguration aus der Sitzung lesen. $conf = Session::getConfig(); @@ -53,7 +53,7 @@ try // Fest eingebaute Standard-Konfiguration laden. $conf = array(); - require('./util/config-default.php'); // writes to $conf + require(OR_MODULES_DIR.'util/config-default.php'); // writes to $conf $customConfig = Configuration::load(); $conf = array_replace_recursive($conf, $customConfig); diff --git a/init.php b/init.php @@ -121,15 +121,11 @@ register_shutdown_function( "fatal_handler" ); require_once( "functions/request.inc.php" ); // Werkzeugklassen einbinden. -require_once( OR_SERVICECLASSES_DIR."include.inc.".PHP_EXT ); +require_once( OR_MODULES_DIR."util/require.".PHP_EXT ); require_once( OR_AUTHCLASSES_DIR."include.inc.".PHP_EXT ); - - - require_once( OR_MODULES_DIR."security/require.".PHP_EXT ); require_once( OR_MODULES_DIR."template-engine/require.".PHP_EXT ); -require_once( OR_MODULES_DIR."util/require.".PHP_EXT ); require_once( OR_MODULES_DIR."configuration/require.".PHP_EXT ); diff --git a/modules/util/.htaccess b/modules/util/.htaccess @@ -0,0 +1,2 @@ +order deny,allow +deny from all+ \ No newline at end of file diff --git a/modules/util/AbstractTree.class.php b/modules/util/AbstractTree.class.php @@ -0,0 +1,171 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Darstellen einer Baumstruktur mit Funktion zum Laden, Oeffnen und Schliessen + * von Teilbaeumen + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class AbstractTree +{ + /** + * Alle Elemente des Baumes + */ + var $elements = array(); + + var $tempElements = array(); + var $userIsAdmin = false; + + var $autoOpen = array(0,1); + + var $opened = array(); + + /** + * Hoechste Element-Id + * @type Integer + */ + var $maxId; + + // Konstruktor + function AbstractTree() + { + // Feststellen, ob der angemeldete Benutzer ein Administrator ist + $user = Session::getUser(); + $this->userIsAdmin = $user->isAdmin; + + // Wurzel-Element laden + $this->root(); + $this->elements[0] = $this->tempElements[0]; + $this->tempElements = array(); + $this->maxId = 0; + + foreach( $this->autoOpen as $openId ) + $this->open($openId); + } + + function refresh() { + + $this->elements = array(); + + // Wurzel-Element laden + $this->root(); + $this->elements[0] = $this->tempElements[0]; + $this->tempElements = array(); + $this->maxId = 0; + + $oids = $this->opened; + $this->opened = array(); + foreach( $oids as $oid) + { + if ( isset($this->elements[$oid]) ) + $this->open($oid); + } + } + + + + function all() { + + $this->elements = array(); + $this->opened = array(); + + // Wurzel-Element laden + $this->root(); + $this->elements[0] = $this->tempElements[0]; + $this->tempElements = array(); + $this->maxId = 0; + + for( $eid=0;isset($this->elements[$eid]); $eid++) + { + $this->open($eid); + } + } + + + /** + * Oeffnen eines Teilbaumes. Es wird der eindeutige Name des zu oeffnenden Teilbaumes als + * Parameter uebergeben + * @param elementName der Name des Elementes, welches zu oeffnen ist + */ + function open( $elementId ) + { + $k = array_search($elementId,$this->opened); + if ( $k !== false ) + return; // Ist schon offen. Evtl. Reload gedrückt? + + $this->opened[] = $elementId; + if ( ! isset($this->elements[$elementId]) ) + return; + $funcName = $this->elements[$elementId]->type; + if ( empty($funcName) ) + return; + + $this->$funcName( $this->elements[$elementId]->internalId ); + + // Wenn keine Unterelemente gefunden, dann die ?ffnen-Funktion deaktivieren + if ( count( $this->tempElements ) == 0 ) + $this->elements[$elementId]->type = ''; + + foreach( $this->tempElements as $treeElement ) + { + $this->maxId++; + $this->elements[$elementId]->subElementIds[] = $this->maxId; + $this->elements[$this->maxId] = $treeElement; + } + + if ( count($this->tempElements)==1 ) + { + $this->tempElements = array(); + $this->open($this->maxId); + } + + $this->tempElements = array(); + } + + + /** + * Schliessen eines Teilbaumes + * @param elementName der Name des Elementes, welches zu schliessen ist + */ + + function close( $elementId ) + { + $this->elements[$elementId]->subElementIds = array(); + + $k = array_search($elementId,$this->opened); + if ( $k !== false ) + unset($this->opened[$k]); + } + + + /** + * Hinzufuegen eines Baum-Elementes + * @param TreeElement Hinzuzufuegendes Baumelement + */ + function addTreeElement( $treeElement ) + { + $this->tempElements[] = $treeElement; + } + + +} + +?>+ \ No newline at end of file diff --git a/modules/util/AdministrationTree.class.php b/modules/util/AdministrationTree.class.php @@ -0,0 +1,594 @@ +<?php +use cms\model\User; +use cms\model\Project; +use cms\model\Group; +use cms\model\Folder; + +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Darstellen einer Baumstruktur mit Administrationfunktionen + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class AdministrationTree extends AbstractTree +{ + /** + * Alle Elemente des Baumes + */ + var $elements; + var $confCache = array(); + + function root() + { + if ( !$this->userIsAdmin ) + Http::notAuthorized('Administration-Tree is only visible for admins.'); + +// $treeElement = new TreeElement(); +// $treeElement->text = lang('GLOBAL_ADMINISTRATION'); +// $treeElement->description = lang('GLOBAL_ADMINISTRATION'); +// $treeElement->type = 'administration'; +// $treeElement->icon = 'administration'; + + $this->administration(); + + $this->autoOpen[] = 1; + } + + + + function administration() + { + global $conf; + $conf_config = $conf['interface']['config']; + + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_PROJECTS'); + $treeElement->description = lang('GLOBAL_PROJECTS'); + $treeElement->url = Html::url('projectlist','show',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'projectlist'; + $treeElement->icon = 'projectlist'; + $treeElement->type = 'projects'; + $treeElement->target = 'cms_main'; + + $this->addTreeElement( $treeElement ); + + + $treeElement = new TreeElement(); + $treeElement->text = lang('USER_AND_GROUPS'); + $treeElement->description = lang('USER_AND_GROUPS'); + $treeElement->icon = 'userlist'; + $treeElement->type = 'userandgroups'; + + $this->addTreeElement( $treeElement ); +// $this->userandgroups(0);; + + if ( $conf_config['enable'] ) + { + $treeElement = new TreeElement(); + $treeElement->text = lang('PREFERENCES'); + $treeElement->description = lang('PREFERENCES'); + $treeElement->icon = 'configuration'; + //$treeElement->type = 'prefs'; + $treeElement->action = 'configuration'; + + $this->addTreeElement( $treeElement ); + } + + // Wechseln zu: Projekte... + /* + foreach( Project::getAll() as $id=>$name ) + { + $treeElement = new TreeElement(); + + $treeElement->text = lang('PROJECT').' '.$name; + $treeElement->url = Html::url(array('action' =>'tree', + 'subaction' =>'reload', + 'projectid' =>$id )); + $treeElement->icon = 'project'; + $treeElement->description = ''; + $treeElement->target = 'cms_tree'; + + $this->addTreeElement( $treeElement ); + } + */ + } + + + + function userandgroups( ) + { + $treeElement = new TreeElement(); + $treeElement->text = lang('GLOBAL_USER'); + $treeElement->description = lang('GLOBAL_USER'); + $treeElement->url = Html::url('user','listing',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'userlist'; + $treeElement->icon = 'userlist'; + $treeElement->target = 'cms_main'; + $treeElement->type = 'users'; + + $this->addTreeElement( $treeElement ); + + $treeElement = new TreeElement(); + $treeElement->text = lang('GLOBAL_GROUPS'); + $treeElement->description = lang('GLOBAL_GROUPS'); + $treeElement->url = Html::url('group','listing',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'grouplist'; + $treeElement->icon = 'userlist'; + $treeElement->target = 'cms_main'; + $treeElement->type = 'groups'; + + $this->addTreeElement( $treeElement ); + } + + + function projects( ) + { + // Schleife ueber alle Projekte + foreach(Project::getAllProjects() as $id=> $name ) + { + $treeElement = new TreeElement(); + + $treeElement->internalId = $id; + $treeElement->id = $id; + $treeElement->text = $name; + $treeElement->url = Html::url('project','edit',$id,array(REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'project'; + $treeElement->action = 'project'; + $treeElement->type = 'project'; + $treeElement->description = ''; + $treeElement->target = 'cms_main'; + + $this->addTreeElement( $treeElement ); + } + } + + + + function project( $projectid ) + { + $project = new Project( $projectid ); + + // Hoechster Ordner der Projektstruktur + $folder = new Folder( $project->getRootObjectId() ); + $folder->load(); + + + // Ermitteln, ob der Benutzer Projektadministrator ist + // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern. + if ( $folder->hasRight( ACL_PROP ) ) + $this->userIsProjectAdmin = true; + + if ( $folder->hasRight( ACL_READ ) ) + { + $treeElement = new TreeElement(); + $treeElement->id = $folder->objectid; + // $treeElement->text = $folder->name; + $treeElement->text = lang('FOLDER_ROOT'); + $treeElement->description = lang('FOLDER_ROOT_DESC'); + $treeElement->icon = 'folder'; + $treeElement->action = 'folder'; + $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') ); + $treeElement->target = 'content'; + $treeElement->type = 'folder'; + $treeElement->internalId = $folder->objectid; + $this->addTreeElement( $treeElement ); + } + + + if ( $this->userIsProjectAdmin ) + { + // Templates + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_TEMPLATES'); + $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->description= lang('GLOBAL_TEMPLATES_DESC'); + $treeElement->icon = 'templatelist'; + $treeElement->action = 'templatelist'; + $treeElement->target = 'content'; + $treeElement->type = 'templates'; + $this->addTreeElement( $treeElement ); + } + + + // Sprachen + $treeElement = new TreeElement(); + $treeElement->description= ''; + $treeElement->id = 0; + $treeElement->action = 'languagelist'; + $treeElement->text = lang('GLOBAL_LANGUAGES'); + $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'languagelist'; + $treeElement->description= lang('GLOBAL_LANGUAGES_DESC'); + $treeElement->target = 'content'; + + // Nur fuer Projekt-Administratoren aufklappbar + if ( $this->userIsProjectAdmin ) + $treeElement->type = 'languages'; + + $this->addTreeElement( $treeElement ); + + + // Projektmodelle + $treeElement = new TreeElement(); + $treeElement->description= ''; + + // Nur fuer Projekt-Administratoren aufklappbar + if ( $this->userIsProjectAdmin ) + $treeElement->type = 'models'; + + $treeElement->id = 0; + $treeElement->description= lang('GLOBAL_MODELS_DESC'); + $treeElement->text = lang('GLOBAL_MODELS'); + $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'modellist'; + $treeElement->icon = 'modellist'; + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + + // Sonstiges + // $treeElement = new TreeElement(); + // $treeElement->text = lang('GLOBAL_OTHER'); + // $treeElement->description= lang('GLOBAL_OTHER_DESC'); + // $treeElement->icon = 'other'; + // $treeElement->type = 'other'; + // $this->addTreeElement( $treeElement ); + + // Suche + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_SEARCH'); + $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'search'; + $treeElement->icon = 'search'; + $treeElement->description = lang('GLOBAL_SEARCH_DESC'); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + } + + + + function prefs_system( ) + { + $system = array( 'time' => date('r'), + 'os' => php_uname('s'), + 'host' => php_uname('n'), + 'release'=> php_uname('r'), + 'machine'=> php_uname('m'), + 'owner' => get_current_user(), + 'pid' => getmypid() ); + + foreach( $system as $key=>$value ) + { + $treeElement = new TreeElement(); + $treeElement->text = $key.'='.$value; + $treeElement->icon = 'config_property'; + $this->addTreeElement( $treeElement ); + $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); + } + + if ( function_exists('getrusage') ) // Funktion existiert auf WIN32 nicht. + { + foreach( getrusage() as $name=>$value ); + { + $treeElement = new TreeElement(); + $treeElement->text = $name.':'.$value; + $treeElement->description = lang('SETTING')." '".$name."'".(!empty($value)?': '.$value:''); + $treeElement->icon = 'config_property'; + $this->addTreeElement( $treeElement ); + } + } + } + + + + + function prefs_php( ) + { + $php_prefs = array( 'version' => phpversion(), + 'SAPI' => php_sapi_name(), + 'session-name' => session_name(), + 'magic_quotes_gpc' => get_magic_quotes_gpc(), + 'magic_quotes_runtime'=> get_magic_quotes_runtime() ); + + foreach( array('upload_max_filesize', + 'file_uploads', + 'memory_limit', + 'max_execution_time', + 'post_max_size', + 'display_errors', + 'register_globals' + ) as $iniName ) + $php_prefs[ $iniName ] = ini_get( $iniName ); + + foreach( $php_prefs as $key=>$value ) + { + $treeElement = new TreeElement(); + $treeElement->text = $key.'='.$value; + $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); + $treeElement->icon = 'config_property'; + $this->addTreeElement( $treeElement ); + } + } + + + + function prefs_extensions( ) + { + $extensions = get_loaded_extensions(); + asort( $extensions ); + + foreach( $extensions as $id=>$extensionName ) + { + $treeElement = new TreeElement(); + $treeElement->text = $extensionName; + $treeElement->icon = 'config_property'; + $treeElement->internalId = $id; + $this->addTreeElement( $treeElement ); + } + } + + + + function prefs_extension( $id ) + { + $extensions = get_loaded_extensions(); + $functions = get_extension_funcs( $extensions[$id] ); + asort( $functions ); + + foreach( $functions as $functionName ) + { + $treeElement = new TreeElement(); + $treeElement->text = $functionName; + $treeElement->icon = 'config_property'; + $this->addTreeElement( $treeElement ); + } + } + + + /** + * Anzeigen von Einstellungen. + * + * @param $id + */ + function prefs( ) + { + global $conf; + + if ( !@$conf['security']['show_system_info'] ) + return; + + $conf_config = $conf['interface']['config']; + + + $treeElement = new TreeElement(); + + $treeElement->internalId = -1; + $treeElement->text = 'OpenRat'; + $treeElement->icon = 'configuration'; + + if ( !empty($conf_config['file_manager_url']) ) + $treeElement->url = $conf_config['file_manager_url']; + $treeElement->target = '_blank'; + $treeElement->description = ''; + $treeElement->type = 'prefs_cms'; + $this->addTreeElement( $treeElement ); + + + + if ( !empty($conf_config['show_system']) ) + { + $treeElement = new TreeElement(); + + $treeElement->internalId = 0; + $treeElement->text = lang('GLOBAL_SYSTEM'); + $treeElement->icon = 'configuration'; + + $treeElement->description = ''; + $treeElement->target = 'cms_main'; + $treeElement->type = 'prefs_system'; + $this->addTreeElement( $treeElement ); + } + + + if ( !empty($conf_config['show_interpreter']) ) + { + $treeElement = new TreeElement(); + + $treeElement->internalId = 0; + $treeElement->text = lang('GLOBAL_PHP'); + $treeElement->icon = 'configuration'; + + $treeElement->description = ''; + $treeElement->target = 'cms_main'; + $treeElement->type = 'prefs_php'; + $this->addTreeElement( $treeElement ); + } + + + if ( !empty($conf_config['show_extensions']) ) + { + $treeElement = new TreeElement(); + + $treeElement->internalId = 0; + $treeElement->text = lang('GLOBAL_EXTENSIONS'); + $treeElement->icon = 'configuration'; + + $treeElement->description = ''; + $treeElement->target = 'cms_main'; + $treeElement->type = 'prefs_extensions'; + $this->addTreeElement( $treeElement ); + } + } + + + function prefs_cms( $id ) + { + global $conf; + + if ( $id < 0 ) + { + $tmpConf = $conf; + } + else + $tmpConf = $this->confCache[$id]; + + if ( !is_array($tmpConf) ) + $tmpConf = array('unknown'); + + foreach( $tmpConf as $key=>$value ) + { + if ( is_array($value) ) + { + $this->confCache[crc32($key)] = $value; + + $treeElement = new TreeElement(); + + $treeElement->internalId = crc32($key); + $treeElement->text = $key; +// if ( $id == 0 ) +// $treeElement->url = Html::url('main','prefs',0,array('conf'=>$key)); + $treeElement->icon = 'configuration'; + + $treeElement->description = count($value).' '.lang('SETTINGS'); + $treeElement->target = 'cms_main'; + $treeElement->type = 'prefs_cms'; + $this->addTreeElement( $treeElement ); + } + else + { + // Die PHP-funktion 'parse_ini_file()' liefert alle Einstellungen leider nur als String + // Daher weiß man hier nicht, ob '1' nun '1' oder 'true' heißen soll. + if ( $value=='' ) + // Anzeige 'Leer' + $value = lang('EMPTY'); + elseif ( $value=='0' ) + // Anzeige 'Nein' + $value = $value.' ('.lang('IS_NO').')'; + elseif ( $value=='1' ) + // Anzeige 'Ja' + $value = '+'.$value.' ('.lang('IS_YES').')'; + elseif ( is_numeric($value) ) + // Anzeige numerische Werte + $value = ($value>0?'+':'').$value; + else + // Anzeige von Zeichenketten + $value = $value; + + $this->confCache[crc32($key)] = $value; + + if ( strpos($key,'pass') !== FALSE ) + $value = '***'; // Kennwörter nicht anzeigen + + $treeElement = new TreeElement(); + $treeElement->text = $key.': '.$value; + $treeElement->icon = 'config_property'; + $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); + + $this->addTreeElement( $treeElement ); + } + } + } + + + function users( ) + { + foreach( User::getAllUsers() as $user ) + { + $treeElement = new TreeElement(); + $treeElement->id = $user->userid; + $treeElement->internalId = $user->userid; + $treeElement->text = $user->name; + $treeElement->url = Html::url('user','edit', + $user->userid,array(REQ_PARAM_TARGET=>'content') ); + $treeElement->action = 'user'; + $treeElement->icon = 'user'; + + $desc = $user->fullname; + + if ( $user->isAdmin ) + $desc .= ' ('.lang('USER_ADMIN').') '; + if ( $user->desc == "" ) + $desc .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + else + $desc .= ' - '.$user->desc; + + $treeElement->description = $desc; + $treeElement->target = 'cms_main'; + + $this->addTreeElement( $treeElement ); + } + } + + + function groups( ) + { + + foreach( Group::getAll() as $id=>$name ) + { + $treeElement = new TreeElement(); + + $g = new Group( $id ); + $g->load(); + + $treeElement->id = $id; + $treeElement->internalId = $id; + $treeElement->text = $g->name; + $treeElement->url = Html::url('group','edit',$id, + array(REQ_PARAM_TARGET=>'content') ); + $treeElement->icon = 'group'; + $treeElement->description = lang('GLOBAL_GROUP').' '.$g->name.': '.implode(', ',$g->getUsers()); + $treeElement->target = 'cms_main'; + $treeElement->type = 'userofgroup'; + $treeElement->action = 'group'; + + $this->addTreeElement( $treeElement ); + } + } + + + function userofgroup( $id ) + { + $g = new Group( $id ); + + foreach( $g->getUsers() as $id=>$name ) + { + $treeElement = new TreeElement(); + + $u = new User( $id ); + $u->load(); + $treeElement->id = $u->userid; + $treeElement->text = $u->name; + $treeElement->url = Html::url('user','edit',$id,array(REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'user'; + $treeElement->action = 'user'; + $treeElement->description = $u->fullname; + $treeElement->target = 'cms_main'; + + $this->addTreeElement( $treeElement ); + } + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Api.class.php b/modules/util/Api.class.php @@ -0,0 +1,101 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +use cms\model\Folder; + + +/** + * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere + * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen + * werden. + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Api +{ + var $output = ''; + var $objectid = 0; + var $page; + + function db() + { + return db_connection(); + } + + function pageid() + { + echo 'WARNING: pageid() deprecated!<br>'; + global $SESS; + return $SESS['objectid']; + } + + function getObjectId() + { + return $this->objectid; + } + + function setObjectId( $objectid ) + { + $this->objectid = $objectid; + } + + function getRootObjectId() + { + return Folder::getRootObjectId(); + } + + function folderid() + { + global $SESS; + return $SESS['folderid']; + } + + + function execute( $code ) + { + global $conf_tmpdir; + $code = "<?php\n".$code."\n?>"; + + $tmp = $conf_tmpdir.'/'.md5(microtime()).'.tmp'; + $f = fopen( $tmp,'w' ); + fwrite( $f,$code ); + fclose( $f ); + + require( $tmp ); // Ausfuehren des temporaeren PHP-Codes + + unlink( $tmp ); + $inhalt = Api::getOutput(); + $this->output( $inhalt ); + } + + function delOutput() + { + $this->output = ''; + } + + function output( $text ) + { + $this->output .= $text; + } + + + function getOutput() + { + return $this->output; + } +}+ \ No newline at end of file diff --git a/modules/util/ArchiveTar.class.php b/modules/util/ArchiveTar.class.php @@ -0,0 +1,397 @@ +<?php +/* +======================================================================= +Name: + tar Class + +Author: + Josh Barger <joshb@npt.com> + +Description: + This class reads and writes Tape-Archive (TAR) Files and Gzip + compressed TAR files, which are mainly used on UNIX systems. + This class works on both windows AND unix systems, and does + NOT rely on external applications!! Woohoo! + +Usage: + Copyright (C) 2002 Josh Barger + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details at: + http://www.gnu.org/copyleft/lesser.html + + If you use this script in your application/website, please + send me an e-mail letting me know about it :) + +Bugs: + Please report any bugs you might find to my e-mail address + at joshb@npt.com. If you have already created a fix/patch + for the bug, please do send it to me so I can incorporate it into my release. + +Version History: + 1.0 04/10/2002 - InitialRelease + + 2.0 04/11/2002 - Merged both tarReader and tarWriter + classes into one + - Added support for gzipped tar files + Remember to name for .tar.gz or .tgz + if you use gzip compression! + :: THIS REQUIRES ZLIB EXTENSION :: + - Added additional comments to + functions to help users + - Added ability to remove files and + directories from archive + 2.1 04/12/2002 - Fixed serious bug in generating tar + - Created another example file + - Added check to make sure ZLIB is + installed before running GZIP + compression on TAR + 2.2 05/07/2002 - Added automatic detection of Gzipped + tar files (Thanks go to J�rgen Falch + for the idea) + - Changed "private" functions to have + special function names beginning with + two underscores +======================================================================= +*/ + + +class ArchiveTar +{ + // Unprocessed Archive Information + var $filename; + var $isGzipped; + var $tar_file; + + // Processed Archive Information + var $files; + var $directories; + var $numFiles; + var $numDirectories; + + + // Class Constructor -- Does nothing... + function tar() { + return true; + } + + + // Computes the unsigned Checksum of a file's header + // to try to ensure valid file + // PRIVATE ACCESS FUNCTION + function __computeUnsignedChecksum($bytestring) + { + $unsigned_chksum=0; + for($i=0; $i<512; $i++) + $unsigned_chksum += ord($bytestring[$i]); + for($i=0; $i<8; $i++) + $unsigned_chksum -= ord($bytestring[148 + $i]); + $unsigned_chksum += ord(" ") * 8; + + return $unsigned_chksum; + } + + + // Converts a NULL padded string to a non-NULL padded string + // PRIVATE ACCESS FUNCTION + function __parseNullPaddedString($string) + { + $position = strpos($string,chr(0)); + return substr($string,0,$position); + } + + + // This function parses the current TAR file + // PRIVATE ACCESS FUNCTION + function __parseTar() + { + // Read Files from archive + $this->numFiles=0; + $tar_length = strlen($this->tar_file); + $main_offset = 0; + while($main_offset < $tar_length) { + // If we read a block of 512 nulls, we are at the end of the archive + if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512)) + break; + + // Parse file name + $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100)); + + // Parse the file mode + $file_mode = substr($this->tar_file,$main_offset + 100,8); + + // Parse the file user ID + $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8)); + + // Parse the file group ID + $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8)); + + // Parse the file size + $file_size = octdec(substr($this->tar_file,$main_offset + 124,12)); + + // Parse the file update time - unix timestamp format + $file_time = octdec(substr($this->tar_file,$main_offset + 136,12)); + + // Parse Checksum + $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6)); + + // Parse user name + $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32)); + + // Parse Group name + $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32)); + + // Make sure our file is valid + if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum) + return false; + + // Parse File Contents + $file_contents = substr($this->tar_file,$main_offset + 512,$file_size); + + /* ### Unused Header Information ### + $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1); + $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100); + $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6); + $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2); + $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8); + $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8); + $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155); + $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12); + */ + + if($file_size > 0) { + // Increment number of files + $this->numFiles++; + + // Create us a new file in our array + $activeFile = &$this->files[]; + + // Asign Values + $activeFile["name"] = $file_name; + $activeFile["mode"] = $file_mode; + $activeFile["size"] = $file_size; + $activeFile["time"] = $file_time; + $activeFile["user_id"] = $file_uid; + $activeFile["group_id"] = $file_gid; + $activeFile["user_name"] = $file_uname; + $activeFile["group_name"] = $file_gname; + $activeFile["checksum"] = $file_chksum; + $activeFile["file"] = $file_contents; + + } else { + // Increment number of directories + $this->numDirectories++; + + // Create a new directory in our array + $activeDir = &$this->directories[]; + + // Assign values + $activeDir["name"] = $file_name; + $activeDir["mode"] = $file_mode; + $activeDir["time"] = $file_time; + $activeDir["user_id"] = $file_uid; + $activeDir["group_id"] = $file_gid; + $activeDir["user_name"] = $file_uname; + $activeDir["group_name"] = $file_gname; + $activeDir["checksum"] = $file_chksum; + } + + // Move our offset the number of blocks we have processed + $main_offset += 512 + (ceil($file_size / 512) * 512); + } + + return true; + } + + + // Read a non gzipped tar file in for processing + // PRIVATE ACCESS FUNCTION + function __readTar($filename='') + { + // Set the filename to load + // Read in the TAR file + + if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) { + if(!function_exists("gzinflate")) + return false; + + $this->isGzipped = TRUE; + + $this->tar_file = gzinflate(substr($this->tar_file,10,-4)); + } + + // Parse the TAR file + $this->__parseTar(); + + return true; + } + + + // Generates a TAR file from the processed data + // PRIVATE ACCESS FUNCTION + function __generateTAR() + { + // Clear any data currently in $this->tar_file + unset($this->tar_file); + + // Generate Records for each directory, if we have directories + if($this->numDirectories > 0) { + foreach($this->directories as $key => $information) { + unset($header); + + // Generate tar header for this directory + // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end + $header .= str_pad($information["name"],100,chr(0)); + $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_repeat(" ",8); + $header .= "5"; + $header .= str_repeat(chr(0),100); + $header .= str_pad("ustar",6,chr(32)); + $header .= chr(32) . chr(0); + $header .= str_pad("",32,chr(0)); + $header .= str_pad("",32,chr(0)); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),155); + $header .= str_repeat(chr(0),12); + + // Compute header checksum + $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); + for($i=0; $i<6; $i++) { + $header[(148 + $i)] = substr($checksum,$i,1); + } + $header[154] = chr(0); + $header[155] = chr(32); + + // Add new tar formatted data to tar file contents + $this->tar_file .= $header; + } + } + + // Generate Records for each file, if we have files (We should...) + if($this->numFiles > 0) + { + foreach($this->files as $key => $information) + { + unset($header); + $header = ''; + + // Generate the TAR header for this file + // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end + $header .= str_pad($information["name"],100,chr(0)); + $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); + $header .= str_repeat(" ",8); + $header .= "0"; + $header .= str_repeat(chr(0),100); + $header .= str_pad("ustar",6,chr(32)); + $header .= chr(32) . chr(0); + $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP? + $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP? + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),8); + $header .= str_repeat(chr(0),155); + $header .= str_repeat(chr(0),12); + + // Compute header checksum + $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); + for($i=0; $i<6; $i++) + { + $header[(148 + $i)] = substr($checksum,$i,1); + } + $header[154] = chr(0); + $header[155] = chr(32); + + // Pad file contents to byte count divisible by 512 + $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0)); + + // Add new tar formatted data to tar file contents + $this->tar_file .= $header . $file_contents; + } + } + + // Add 512 bytes of NULLs to designate EOF + $this->tar_file .= str_repeat(chr(0),512); + + return true; + } + + + // Open a TAR file + function openTAR($value) + { + // Clear any values from previous tar archives + unset($this->filename); + unset($this->isGzipped); + unset($this->tar_file); + unset($this->files); + unset($this->directories); + unset($this->numFiles); + unset($this->numDirectories); + + $this->filename = 'none'; + $this->tar_file = $value; + // Parse this file + $this->__readTar(); + + return true; + } + + + // Write the currently loaded tar archive to disk + function saveTar() + { + if(!$this->filename) + return false; + + // Write tar to current file using specified gzip compression + $this->toTar($this->filename,$this->isGzipped); + + return true; + } + + + // Saves tar archive to a different file than the current file + function toTar($filename,$useGzip) + { + if(!$filename) + return false; + + // Encode processed files into TAR file format + $this->__generateTar(); + + // GZ Compress the data if we need to + if($useGzip) { + // Make sure we have gzip support + if(!function_exists("gzencode")) + return false; + + $file = gzencode($this->tar_file); + } else { + $file = $this->tar_file; + } + + // Write the TAR file + $fp = fopen($filename,"wb"); + fwrite($fp,$file); + fclose($fp); + + return true; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/ArchiveUnzip.class.php b/modules/util/ArchiveUnzip.class.php @@ -0,0 +1,447 @@ +<?php +// 28/11/2005 (2.4) +// - dUnzip2 is now compliant with wrong placed "Data Description", made by some compressors, +// like the classes ZipLib and ZipLib2 by 'Hasin Hayder'. Thanks to Ricardo Parreno for pointing it. +// 09/11/2005 (2.3) +// - Added optional parameter '$stopOnFile' on method 'getList()'. +// If given, file listing will stop when find given filename. (Useful to open and unzip an exact file) +// 06/11/2005 (2.21) +// - Added support to PK00 file format (Packed to Removable Disk) (thanks to Lito [PHPfileNavigator]) +// - Method 'getExtraInfo': If requested file doesn't exist, return FALSE instead of Array() +// 31/10/2005 (2.2) +// - Removed redundant 'file_name' on centralDirs declaration (thanks to Lito [PHPfileNavigator]) +// - Fixed redeclaration of file_put_contents when in PHP4 (not returning true) + +############################################################## +# Class dUnzip2 v2.4 +# +# Author: Alexandre Tedeschi (d) +# E-Mail: alexandrebr at gmail dot com +# Londrina - PR / Brazil +# +# Objective: +# This class allows programmer to easily unzip files on the fly. +# +# Requirements: +# This class requires extension ZLib Enabled. It is default +# for most site hosts around the world, and for the PHP Win32 dist. +# +# To do: +# * Error handling +# * Write a PHP-Side gzinflate, to completely avoid any external extensions +# * Write other decompress algorithms +# +# If you modify this class, or have any ideas to improve it, please contact me! +# You are allowed to redistribute this class, if you keep my name and contact e-mail on it. +############################################################## + +class ArchiveUnzip{ + + // Public + var $files = array(); + var $value = ''; + var $fileName; + var $compressedList; // You will problably use only this one! + var $centralDirList; // Central dir list... It's a kind of 'extra attributes' for a set of files + var $endOfCentral; // End of central dir, contains ZIP Comments + var $debug; + + // Private + var $fh; + var $zipSignature = "\x50\x4b\x03\x04"; // local file header signature + var $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature + var $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature + + // Public + Function ArchiveUnzip() + { + $this->compressedList = + $this->centralDirList = + $this->endOfCentral = Array(); + } + + function open( $value ) + { + $this->fileName = tempnam('/tmp','unzip'); +// echo $this->fileName; + $fo = fopen( $this->fileName,'w'); + fwrite($fo,$value); + $this->unzipAll(); + } + + + Function getList($stopOnFile=false){ + if(sizeof($this->compressedList)){ + $this->debugMsg(1, "Returning already loaded file list."); + return $this->compressedList; + } + + // Open file, and set file handler + $fh = fopen($this->fileName, "r"); + $this->fh = &$fh; + if(!$fh){ + $this->debugMsg(2, "Failed to load file."); + return false; + } + + // Loop the file, looking for files and folders + $ddTry = false; + fseek($fh, 0); + for(;;){ + // Check if the signature is valid... + $signature = fread($fh, 4); + if(feof($fh)){ +# $this->debugMsg(1, "Reached end of file"); + break; + } + + // If signature is a 'Packed to Removable Disk', just ignore it and move to the next. + if($signature == 'PK00'){ + $this->debugMsg(1, "Found PK00: Packed to Removable Disk"); + continue; + } + + // If signature of a 'Local File Header' + if($signature == $this->zipSignature){ + # $this->debugMsg(1, "Zip Signature!"); + + // Get information about the zipped file + $file['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract + $file['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag + $file['compression_method'] = unpack("v", fread($fh, 2)); // compression method + $file['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time + $file['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date + $file['crc-32'] = fread($fh, 4); // crc-32 + $file['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size + $file['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size + $fileNameLength = unpack("v", fread($fh, 2)); // filename length + $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length + $file['file_name'] = fread($fh, $fileNameLength[1]); // filename + $file['extra_field'] = $extraFieldLength[1]?fread($fh, $extraFieldLength[1]):''; // extra field + $file['contents-startOffset']= ftell($fh); + + // Bypass the whole compressed contents, and look for the next file + fseek($fh, $file['compressed_size'][1], SEEK_CUR); + + // Convert the date and time, from MS-DOS format to UNIX Timestamp + $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT); + $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT); + $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; + $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); + $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); + $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); + $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); + $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)); + + // Mount file table + $this->compressedList[$file['file_name']] = Array( + 'file_name' =>$file['file_name'], + 'compression_method'=>$file['compression_method'][1], + 'version_needed' =>$file['version_needed'][1], + 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY), + 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size' =>$file['compressed_size'][1], + 'uncompressed_size' =>$file['uncompressed_size'][1], + 'extra_field' =>$file['extra_field'], + 'general_bit_flag' =>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'contents-startOffset'=>$file['contents-startOffset'] + ); + + if($stopOnFile) if($file['file_name'] == $stopOnFile){ + $this->debugMsg(1, "Stopping on file..."); + break; + } + } + + // If signature of a 'Central Directory Structure' + elseif($signature == $this->dirSignature){ + # $this->debugMsg(1, "Dir Signature!"); + + $dir['version_madeby'] = unpack("v", fread($fh, 2)); // version made by + $dir['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract + $dir['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag + $dir['compression_method'] = unpack("v", fread($fh, 2)); // compression method + $dir['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time + $dir['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date + $dir['crc-32'] = fread($fh, 4); // crc-32 + $dir['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size + $dir['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size + $fileNameLength = unpack("v", fread($fh, 2)); // filename length + $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length + $fileCommentLength = unpack("v", fread($fh, 2)); // file comment length + $dir['disk_number_start'] = unpack("v", fread($fh, 2)); // disk number start + $dir['internal_attributes'] = unpack("v", fread($fh, 2)); // internal file attributes-byte1 + $dir['external_attributes1']= unpack("v", fread($fh, 2)); // external file attributes-byte2 + $dir['external_attributes2']= unpack("v", fread($fh, 2)); // external file attributes + $dir['relative_offset'] = unpack("V", fread($fh, 4)); // relative offset of local header + $dir['file_name'] = fread($fh, $fileNameLength[1]); // filename + $dir['extra_field'] = $extraFieldLength[1] ?fread($fh, $extraFieldLength[1]) :''; // extra field + $dir['file_comment'] = $fileCommentLength[1]?fread($fh, $fileCommentLength[1]):''; // file comment + + // Convert the date and time, from MS-DOS format to UNIX Timestamp + $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT); + $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT); + $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; + $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); + $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); + $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); + $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); + $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)); + + $this->centralDirList[$dir['file_name']] = Array( + 'version_madeby'=>$dir['version_madeby'][1], + 'version_needed'=>$dir['version_needed'][1], + 'general_bit_flag'=>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), + 'compression_method'=>$dir['compression_method'][1], + 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY), + 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). + str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), + 'compressed_size'=>$dir['compressed_size'][1], + 'uncompressed_size'=>$dir['uncompressed_size'][1], + 'disk_number_start'=>$dir['disk_number_start'][1], + 'internal_attributes'=>$dir['internal_attributes'][1], + 'external_attributes1'=>$dir['external_attributes1'][1], + 'external_attributes2'=>$dir['external_attributes2'][1], + 'relative_offset'=>$dir['relative_offset'][1], + 'file_name'=>$dir['file_name'], + 'extra_field'=>$dir['extra_field'], + 'file_comment'=>$dir['file_comment'], + ); + } + + elseif($signature == $this->dirSignatureE){ + # $this->debugMsg(1, "EOF Dir Signature!"); + + $eodir['disk_number_this'] = unpack("v", fread($fh, 2)); // number of this disk + $eodir['disk_number'] = unpack("v", fread($fh, 2)); // number of the disk with the start of the central directory + $eodir['total_entries_this'] = unpack("v", fread($fh, 2)); // total number of entries in the central dir on this disk + $eodir['total_entries'] = unpack("v", fread($fh, 2)); // total number of entries in + $eodir['size_of_cd'] = unpack("V", fread($fh, 4)); // size of the central directory + $eodir['offset_start_cd'] = unpack("V", fread($fh, 4)); // offset of start of central directory with respect to the starting disk number + $zipFileCommentLenght = unpack("v", fread($fh, 2)); // zipfile comment length + $eodir['zipfile_comment'] = $zipFileCommentLenght[1]?fread($fh, $zipFileCommentLenght[1]):''; // zipfile comment + $this->endOfCentral = Array( + 'disk_number_this'=>$eodir['disk_number_this'][1], + 'disk_number'=>$eodir['disk_number'][1], + 'total_entries_this'=>$eodir['total_entries_this'][1], + 'total_entries'=>$eodir['total_entries'][1], + 'size_of_cd'=>$eodir['size_of_cd'][1], + 'offset_start_cd'=>$eodir['offset_start_cd'][1], + 'zipfile_comment'=>$eodir['zipfile_comment'], + ); + } + else{ + if(!$ddTry){ + $this->debugMsg(1, "Unexpected header. Trying to detect wrong placed 'Data Descriptor'...\n"); + $ddTry = true; + fseek($fh, 12-4, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4) + continue; + } + $this->debugMsg(1, "Unexpected header, ending loop at offset ".ftell($fh)); + break; + } + $ddTry = false; + } + + if($this->debug){ + #------- Debug compressedList + $kkk = 0; + echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>"; + foreach($this->compressedList as $fileName=>$item){ + if(!$kkk && $kkk=1){ + echo "<tr style='background: #ADA'>"; + foreach($item as $fieldName=>$value) + echo "<td>$fieldName</td>"; + echo '</tr>'; + } + echo "<tr style='background: #CFC'>"; + foreach($item as $fieldName=>$value){ + if($fieldName == 'lastmod_datetime') + echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>"; + else + echo "<td title='$fieldName' nowrap='nowrap'>$value</td>"; + } + echo "</tr>"; + } + echo "</table>"; + + #------- Debug centralDirList + $kkk = 0; + if(sizeof($this->centralDirList)){ + echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>"; + foreach($this->centralDirList as $fileName=>$item){ + if(!$kkk && $kkk=1){ + echo "<tr style='background: #AAD'>"; + foreach($item as $fieldName=>$value) + echo "<td>$fieldName</td>"; + echo '</tr>'; + } + echo "<tr style='background: #CCF'>"; + foreach($item as $fieldName=>$value){ + if($fieldName == 'lastmod_datetime') + echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>"; + else + echo "<td title='$fieldName' nowrap='nowrap'>$value</td>"; + } + echo "</tr>"; + } + echo "</table>"; + } + + #------- Debug endOfCentral + $kkk = 0; + if(sizeof($this->endOfCentral)){ + echo "<table border='0' style='font: 11px Verdana' style='border: 1px solid #000'>"; + echo "<tr style='background: #DAA'><td colspan='2'>dUnzip - End of file</td></tr>"; + foreach($this->endOfCentral as $field=>$value){ + echo "<tr>"; + echo "<td style='background: #FCC'>$field</td>"; + echo "<td style='background: #FDD'>$value</td>"; + echo "</tr>"; + } + echo "</table>"; + } + } + + return $this->compressedList; + } + + + Function getExtraInfo($compressedFileName) + { + return + isset($this->centralDirList[$compressedFileName])? + $this->centralDirList[$compressedFileName]: + false; + } + + + Function getZipInfo($detail=false) + { + return $detail? + $this->endOfCentral[$detail]: + $this->endOfCentral; + } + + + Function unzip($compressedFileName, $targetFileName=false){ + $fdetails = &$this->compressedList[$compressedFileName]; + + if(!sizeof($this->compressedList)){ + $this->debugMsg(1, "Trying to unzip before loading file list... Loading it!"); + $this->getList(false, $compressedFileName); + } + if(!isset($this->compressedList[$compressedFileName])){ + $this->debugMsg(2, "File '<b>$compressedFileName</b>' is not compressed in the zip."); + return false; + } + if(substr($compressedFileName, -1) == "/"){ + $this->debugMsg(2, "Trying to unzip a folder name '<b>$compressedFileName</b>'."); + return false; + } + if(!$fdetails['uncompressed_size']){ + $this->debugMsg(1, "File '<b>$compressedFileName</b>' is empty."); + return ""; + } + + fseek($this->fh, $fdetails['contents-startOffset']); + return $this->uncompress( + fread($this->fh, $fdetails['compressed_size']), + $fdetails['compression_method'], + $fdetails['uncompressed_size'] ); + } + + + Function unzipAll($targetDir=false, $baseDir="", $maintainStructure=true, $chmod=false){ + if($targetDir === false) + $targetDir = dirname(__FILE__)."/"; + + $lista = $this->getList(); + if(sizeof($lista)) foreach($lista as $fileName=>$trash){ + $dirname = dirname($fileName); + $outDN = "$targetDir/$dirname"; + + if(substr($dirname, 0, strlen($baseDir)) != $baseDir) + continue; + + if(!is_dir($outDN) && $maintainStructure){ + $str = ""; + $folders = explode("/", $dirname); + foreach($folders as $folder){ + $str = $str?"$str/$folder":$folder; + if(!is_dir("$targetDir/$str")){ + $this->debugMsg(1, "Creating folder: $targetDir/$str"); + mkdir("$targetDir/$str"); + if($chmod) + chmod("$targetDir/$str", $chmod); + } + } + } + if(substr($fileName, -1, 1) == "/") + continue; + + $maintainStructure? + $this->unzip($fileName, "$targetDir/$fileName"): + $this->unzip($fileName, "$targetDir/".basename($fileName)); + + if($chmod) + chmod($maintainStructure?"$targetDir/$fileName":"$targetDir/".basename($fileName), $chmod); + } + } + + Function close(){ // Free the file resource + if($this->fh) + fclose($this->fh); + } + + // Private (you should NOT call these methods): + Function uncompress($content, $mode, $uncompressedSize, $targetFileName=false){ + switch($mode){ + case 0: + // Not compressed + return $content; + case 1: + $this->debugMsg(2, "Shrunk mode is not supported... yet?"); + return false; + case 2: + case 3: + case 4: + case 5: + $this->debugMsg(2, "Compression factor ".($mode-1)." is not supported... yet?"); + return false; + case 6: + $this->debugMsg(2, "Implode is not supported... yet?"); + return false; + case 7: + $this->debugMsg(2, "Tokenizing compression algorithm is not supported... yet?"); + return false; + case 8: + // Deflate + return gzinflate($content, $uncompressedSize); + case 9: + $this->debugMsg(2, "Enhanced Deflating is not supported... yet?"); + return false; + case 10: + $this->debugMsg(2, "PKWARE Date Compression Library Impoloding is not supported... yet?"); + return false; + default: + $this->debugMsg(2, "Unknown uncompress method: $mode"); + return false; + } + } + + + Function debugMsg($level, $string){ + if($this->debug) + if($level == 1) + echo "<b style='color: #777'>dUnzip2:</b> $string<br>"; + if($level == 2) + echo "<b style='color: #F00'>dUnzip2:</b> $string<br>"; + } +} +?>+ \ No newline at end of file diff --git a/modules/util/ArchiveZip.class.php b/modules/util/ArchiveZip.class.php @@ -0,0 +1,89 @@ +<?php + + +/** + * This source is taken from http://www.zend.com/zend/spotlight/creating-zip-files1.php + * Thank you! + */ +class ArchiveZip +{ + var $datasec = array(); + var $ctrl_dir = array(); + var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; + var $old_offset = 0; + + + function add_file($data, $name) + { + $name = str_replace("\\", "/", $name); + $unc_len = strlen($data); + $crc = crc32($data); + $zdata = gzcompress($data); + $zdate = substr ($zdata, 2, -4); + $c_len = strlen($zdata); + + + + $fr = "\x50\x4b\x03\x04"; + $fr .= "\x14\x00"; + $fr .= "\x00\x00"; + $fr .= "\x08\x00"; + $fr .= "\x00\x00\x00\x00"; + $fr .= pack("V",$crc); + $fr .= pack("V",$c_len); + $fr .= pack("V",$unc_len); + $fr .= pack("v", strlen($name) ); + $fr .= pack("v", 0 ); + $fr .= $name; + $fr .= $zdata; + $fr .= pack("V",$crc); + $fr .= pack("V",$c_len); + $fr .= pack("V",$unc_len); + + $this -> datasec[] = $fr; + + + + $new_offset = strlen(implode("", $this->datasec)); + + $cdrec = "\x50\x4b\x01\x02"; + $cdrec .="\x00\x00"; + $cdrec .="\x14\x00"; + $cdrec .="\x00\x00"; + $cdrec .="\x08\x00"; + $cdrec .="\x00\x00\x00\x00"; + $cdrec .= pack("V",$crc); + $cdrec .= pack("V",$c_len); + $cdrec .= pack("V",$unc_len); + $cdrec .= pack("v", strlen($name) ); + $cdrec .= pack("v", 0 ); + $cdrec .= pack("v", 0 ); + $cdrec .= pack("v", 0 ); + $cdrec .= pack("v", 0 ); + $cdrec .= pack("V", 32 ); + $cdrec .= pack("V", $this -> old_offset ); + + $this -> old_offset = $new_offset; + + $cdrec .= $name; + $this -> ctrl_dir[] = $cdrec; + } + + + function file() { + $data = implode("", $this -> datasec); + $ctrldir = implode("", $this -> ctrl_dir); + + return + $data. + $ctrldir. + $this -> eof_ctrl_dir. + pack("v", sizeof($this -> ctrl_dir)). + pack("v", sizeof($this -> ctrl_dir)). + pack("V", strlen($ctrldir)). + pack("V", strlen($data)). + "\x00\x00"; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Code.class.php b/modules/util/Code.class.php @@ -0,0 +1,44 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Code extends Macro +{ + var $code = ''; + + function execute() + { + if ( substr($this->code,0,5) != '<?php' ) + $this->code = "<?php\n".$this->code."\n?>"; + + $tmp = FileUtils::getTempDir().'/openratMacro'; + $tmp .= '.code.php.tmp'; + + $f = fopen( $tmp,'w' ); + fwrite( $f,$this->code ); + fclose( $f ); + + require( $tmp ); // Ausfuehren des temporaeren PHP-Codes + + unlink( $tmp ); + } +}+ \ No newline at end of file diff --git a/modules/util/Dynamic.class.php b/modules/util/Dynamic.class.php @@ -0,0 +1,34 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2013 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Oberklasse für dynamische Klassen ("Makros"). + * + * Diese Klasse dient nur der Abwärtskompabilität. Neue dynamische Klassen + * (ab jetzt "Makros") sollten von der Klasse Macro erben. + * + * @author Jan Dankert + * @package openrat.services + */ +class Dynamic extends Macro +{ + // Keine weitere Implementierung +} + +?>+ \ No newline at end of file diff --git a/modules/util/FileUtils.class.php b/modules/util/FileUtils.class.php @@ -0,0 +1,102 @@ +<?php + +/** + * Werkzeugklasse f�r Datei-Operationen. + * + */ +class FileUtils +{ + /** + * Fuegt einen Slash ("/") an das Ende an, sofern nicht bereits vorhanden. + * + * @param String $pfad + * @return Pfad mit angeh�ngtem Slash. + */ + public static function slashify($pfad) + { + if ( substr($pfad,-1,1) == '/') + return $pfad; + else + return $pfad.'/'; + } + + + + /** + * Liefert einen Verzeichnisnamen fuer temporaere Dateien. + */ + public static function createTempFile() + { + global $conf; + $tmpdir = @$conf['cache']['tmp_dir']; + $tmpfile = @tempnam( $tmpdir,'openrat_tmp' ); + + // 2. Versuch: Temp-Dir aus "upload_tmp_dir". + if ( $tmpfile === FALSE ) + { + $tmpdir = ini_get('upload_tmp_dir'); + $tmpfile = @tempnam( $tmpdir,'openrat_tmp' ); + } + + elseif ( $tmpfile === FALSE ) + { + $tmpfile = @tempnam( '','openrat_tmp' ); + } + + return $tmpfile; + } + + + /** + * Liefert einen Verzeichnisnamen fuer temporaere Dateien. + */ + public static function getTempDir() + { + $tmpfile = FileUtils::createTempFile(); + + $tmpdir = dirname($tmpfile); + @unlink($tmpfile); + + return FileUtils::slashify( $tmpdir ); + } + + + + /** + * Liest die Dateien aus dem angegebenen Ordner in ein Array. + * + * @param $dir Verzeichnis, welches gelesen werden soll + * @return Array Liste der Dateien im Ordner + */ + public static function readDir($dir) + { + $dir = FileUtils::slashify($dir); + $dateien = array(); + + if ( !is_dir($dir) ) + { + return false; + } + + if ( $dh = opendir($dir) ) + { + while( ($verzEintrag = readdir($dh)) !== false ) + { + if ( substr($verzEintrag,0,1) != '.' ) + { + $dateien[] = $verzEintrag; + } + } + closedir($dh); + + return $dateien; + } + else + { + die('unable to open directory: '.$dir); + } + + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Ftp.class.php b/modules/util/Ftp.class.php @@ -0,0 +1,246 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Darstellen einer FTP-Verbindung, das beinhaltet + * das Login, das Kopieren von Dateien sowie praktische + * FTP-Funktionen + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Ftp +{ + var $verb; + var $url; + var $log = array(); + + var $passive = false; + + var $ok = true; + + private $path; + + + // Konstruktor + function __construct( $url ) + { + $this->connect( $url ); + } + + + // Aufbauen der Verbindung + function connect( $url ) + { + $this->url = $url; + + global $conf; + + $conf_ftp = $conf['publish']['ftp']; + $ftp = parse_url( $this->url ); + + // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten + $ftp = array_merge($conf_ftp,$ftp); + + // Nur FTP und FTPS (seit PHP 4.3) erlaubt + if ( !in_array(@$ftp['scheme'],array('ftp','ftps')) ) + { + $this->log[] = 'Unknown scheme in FTP Url: '.@$ftp['scheme']; + $this->log[] = 'Only FTP (and FTPS, if compiled in) are supported'; + $this->ok = false; + return; + } + + if ( function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps' ) + $this->verb = @ftp_ssl_connect( $ftp['host'],$ftp['port'] ); + else + $this->verb = @ftp_connect( $ftp['host'],$ftp['port'] ); + + if ( !$this->verb ) + { + $this->log[] = 'Cannot connect to '.$ftp['scheme'].'-server: '.$ftp['host'].':'.$ftp['port']; + $this->ok = false; + + Logger::error('Cannot connect to '.$ftp['host'].':'.$ftp['port']); + return; + } + + $this->log[] = 'Connected to FTP server '.$ftp['host'].':'.$ftp['port']; + + if ( empty($ftp['user']) ) + { + $ftp['user'] = 'anonymous'; + $ftp['pass'] = 'openrat@openrat.de'; + } + + if ( ! ftp_login( $this->verb,$ftp['user'],$ftp['pass'] ) ) + { + $this->log[] = 'Unable to login as user '.$ftp['user']; + $this->ok = false; + return; + } + + $this->log[] = 'Logged in as user '.$ftp['user']; + + $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive' ); + + $this->log[] = 'entering passive mode '.($pasv?'on':'off'); + if ( ! ftp_pasv($this->verb,true) ) + { + $this->log[] = 'cannot switch PASV mode'; + $this->ok = false; + return; + } + + if ( !empty($ftp['query']) ) + { + parse_str( $ftp['query'],$ftp_var ); + + if ( isset( $ftp_var['site'] ) ) + { + $site_commands = explode( ',',$ftp_var['site'] ); + foreach( $site_commands as $cmd ) + { + $this->log .= 'executing SITE command: '.$cmd; + + if ( ! @ftp_site( $this->verb,$cmd ) ) + { + $this->log[] = 'unable to do SITE command: '.$cmd; + $this->ok = false; + return; + } + } + } + } + + $this->path = rtrim( $ftp['path'],'/' ); + + $this->log[] = 'Changing directory to '.$this->path; + + if ( ! @ftp_chdir( $this->verb,$this->path ) ) + { + $this->log[] = 'unable CHDIR to directory: '.$this->path; + $this->ok = false; + return; + } + } + + + /** + * Kopieren einer Datei vom lokalen System auf den FTP-Server. + * + * @param String Quelle + * @param String Ziel + * @param int FTP-Mode (BINARY oder ASCII) + */ + function put( $source,$dest ) + { + if ( ! $this->ok ) + return; + + $ftp = parse_url( $this->url ); + + $dest = $this->path.'/'.$dest; + + $this->log .= "Copying file: $source -&gt; $dest ...\n"; + + $mode = FTP_BINARY; + $p = strrpos( basename($dest),'.' ); // Letzten Punkt suchen + + if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen + { + $extension = substr( basename($dest),$p+1 ); + $type = config('mime-types',$extension); + if ( substr($type,0,5) == 'text/') + $mode = FTP_ASCII; + } + + Logger::debug("FTP PUT target:$dest mode:".(($mode==FTP_ASCII)?'ascii':'binary')); + + if ( !@ftp_put( $this->verb,$dest,$source,$mode ) ) + { + if ( !$this->mkdirs( dirname($dest) ) ) + return; // Fehler. + + ftp_chdir( $this->verb,$this->path ); + + if ( ! @ftp_put( $this->verb,$dest,$source,$mode ) ) + { + $this->ok = false; + $this->log[] = 'FTP PUT failed...'; + $this->log[] = 'source : '.$source; + $this->log[] = 'destination: '.$dest; + return; + } + + } + } + + + + /** + * Private Methode zum rekursiven Anlegen von Verzeichnissen. + * + * @param String Pfad + * @return boolean true, wenn ok + */ + function mkdirs( $strPath ) + { + if ( @ftp_chdir($this->verb,$strPath) ) + return true; // Verzeichnis existiert schon :) + + $pStrPath = dirname($strPath); + + if ( !$this->mkdirs($pStrPath) ) + return false; + + if ( ! @ftp_mkdir($this->verb,$strPath) ) + { + $this->ok = false; + $this->log[] = "failed to create remote directory: $strPath"; + } + + return $this->ok; + } + + + + /** + * Schlie�en der FTP-Verbindung.<br> + * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. + */ + function close() + { + if ( !$this->ok ) // Noch alles ok? + return; + + if ( ! @ftp_quit( $this->verb ) ) + { + // Das Schlie�en der Verbindung hat nicht funktioniert. + // Eigentlich k�nnten wir das ignorieren, aber wir sind anst�ndig und melden eine Fehler. + $this->log[] = 'failed to close connection'; + $this->ok = false; + return; + } + } +} + + +?>+ \ No newline at end of file diff --git a/modules/util/GlobalFunctions.class.php b/modules/util/GlobalFunctions.class.php @@ -0,0 +1,71 @@ +<?php + +/** + * Bereitstellen von globalen Funktionen + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class GlobalFunctions +{ + public static function getIsoCodes() + { + global $conf_php; + + $iso = parse_ini_file( './language/lang.ini.'.$conf_php ); + asort( $iso ); + return $iso; + } + + + public static function lang( $text ) + { + global $SESS; + $text = strtoupper($text); + + if ( isset( $SESS['lang'][$text] ) ) + { + return $SESS['lang'][$text]; + } + else + { + return( '?'.$text.'?' ); + } + } + + + # Spracheinstellungen laden + + public static function language_from_http() + { + global $SESS, + $HTTP_SERVER_VARS, + $conf_php, + $conf; + + $languages = $HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE']; + $languages = explode(',',$languages); + foreach( $languages as $l ) + { + $l = substr($l,0,2); + if ( file_exists("./language/$l.ini.$conf_php") ) + return( $l ); + } + + // Keine passende Sprache im HTTP-Header gefunden + return $conf['global']['default_language']; + } + + + public static function language_read( $l='' ) + { + global $SESS, + $HTTP_SERVER_VARS, + $conf_php; + + $l = language_from_http(); + $SESS['lang'] = parse_ini_file( "./language/$l.ini.$conf_php" ); + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Html.class.php b/modules/util/Html.class.php @@ -0,0 +1,178 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Bereitstellen von Methoden fuer die Darstellung von HTML-Elementen + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Html +{ + public static function error( $field ) + { + global $inputErrors; + + if ( isset($inputErrors[$field]) ) + return '<span class="error">'.lang($inputErrors[$field]).'</span'; + } + + + + /** + * Ausgabe eines Variablenwertes.<br> + */ + public static function debug( $wert, $text='' ) + { + echo "<strong>DEBUG: $text (".gettype($wert).")</strong><br/>"; + echo "<pre>"; + print_r($wert); + echo "</pre>"; + } + + + /** + * Erzeugt eine relative Url innerhalb von Openrat + * + * @param string Aktion, die aufgerufen werden soll + * @param string Unteraktion, die innerhalb der Aktion aufgerufen werden soll + * @param int Id fuer diesen Aufruf + * @param array Weitere beliebige Parameter + */ + public static function url( $action,$subaction='',$id='',$params=array() ) + { + if ( intval($id)==0 ) + $id='-'; + + global $conf; + + if ( is_array($action) ) + { + $params = $action; + + if ( isset($params['callAction']) ) + { + $params['subaction'] = $params['callAction']; + unset( $params['callAction'] ); + unset( $params['callSubaction'] ); + } + + + if ( !isset($params['action' ])) $params['action' ] = ''; + if ( !isset($params['subaction'])) $params['subaction'] = ''; + if ( !isset($params['id' ])) $params['id' ] = ''; + $action = $params['action' ]; + $subaction = $params['subaction']; + $id = $params['id' ]; + unset( $params['action' ] ); + unset( $params['subaction'] ); + unset( $params['id' ] ); + $params['old']='true'; + } + + // Session-Id ergaenzen + if ( $conf['interface']['url']['add_sessionid'] ) + $params[ session_name() ] = session_id(); + + if ( config('security','use_post_token') ) + $params[ 'token'] = token(); + + $fake_urls = $conf['interface']['url']['fake_url' ]; + $url_format = $conf['interface']['url']['url_format']; + + if ( isset($params['objectid']) && !isset($params['id']) ) + $params['id'] = $params['objectid']; + + if ( $fake_urls ) + { +// if ( $id != '' ) +// $id = '.'.$id; + } + else + { + global $view; + $params[REQ_PARAM_ACTION ] = $action; + $params[REQ_PARAM_SUBACTION] = $subaction; + $params[REQ_PARAM_ID ] = $id; + + if ( !isset($params[REQ_PARAM_TARGET])) + $params[REQ_PARAM_TARGET ] = $view; + } + + if ( count($params) > 0 ) + { + $urlParameterList = array(); + foreach( $params as $var=>$value ) + { + $urlParameterList[] = urlencode($var).'='.urlencode($value); + } + $urlParameter = '?'.implode('&amp;',$urlParameterList); + } + else + { + $urlParameter = ''; + } + + if ( @$conf['interface']['url']['index'] ) + $controller_file_name = ''; + else + $controller_file_name = OR_CONTROLLER_FILE.'.'.PHP_EXT; + + $prefix = './'; + + if ( $fake_urls ) + $src = sprintf( $url_format,$action,$subaction,$id,session_id() ).$urlParameter; + else + $src = $prefix.$controller_file_name.$urlParameter; + + return $src; + } + + + + public static function complete_tag($tagname,$attributes) + { + $text = '<'.$tagname; + foreach( $attributes as $attribute_name=>$attribute_value ) + if ( !empty($attribute_value) ) + $text .= ' '.$attribute_name.'="'.$attribute_value.'"'; + $text .= ' />'; + return $text; + } + + + + public static function open_tag($tagname,$attributes) + { + $text = '<'.$tagname; + foreach( $attributes as $attribute_name=>$attribute_value ) + if ( !empty($attribute_value) ) + $text .= ' '.$attribute_name.'="'.$attribute_value.'"'; + $text .= '>'; + return $text; + } + + + public static function close_tag($tagname) + { + return '</'.$tagname.'>'; + } +} +?>+ \ No newline at end of file diff --git a/modules/util/Http.class.php b/modules/util/Http.class.php @@ -0,0 +1,610 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Kapselung einer HTTP-Anfrage.<br> + * Unter Beruecksichtigung von RFC 1945.<br> + * + * @author Jan Dankert + * @package openrat.services + */ +class Http +{ + public $header = array(); + + public $url = array(); + public $responseHeader = array(); + public $requestParameter = array(); + public $urlParameter = array(); + + /** + * HTTP-Request-Typ.<br> + * Muss entweder "GET" oder "POST" sein.<br> + * Default: "GET". + * + * @var String Request-Typ + */ + public $method = 'GET'; + public $error = ''; + public $status = ''; + public $body = ''; + + public $httpCmd = ''; + + + + /** + * Erzeugt eine HTTP-Anfrage. + * + * @param String URL + * @return Http + */ + public function Http( $url = '' ) + { + $this->setURL( $url ); + $this->header['User-Agent'] = 'Mozilla/5.0 (OpenRat CMS)'; + $this->header['Connection'] = 'close'; + } + + + + /** + * Setzt die URL. + * + * @param String URL + */ + function setURL( $url ) + { + $this->url = parse_url($url); + + if ( empty($this->url['host']) && !empty($this->url['path']) ) + { + $this->url['host'] = basename($this->url['path']); + $this->url['path'] = '/'; + } + + if ( empty($this->url['path']) ) + $this->url['path'] = '/'; + + if ( !isset($this->url['port']) ) + if ( !isset($this->url['scheme']) ) + { + $this->url['scheme'] = 'http'; // Standard-Port. + $this->url['port'] = 80; // Standard-Port. + } + elseif ( $this->url['scheme'] == 'https' ) + $this->url['port'] = 443; // SSL-Port. + else + $this->url['port'] = 80; // Standard-Port. + + if ( !empty($this->url['query']) ) + parse_str( $this->url['query'],$this->urlParameter ); + + } + + + + /** + * Setzt Authentisierungsinformationen in den HTTP-Request.<br> + * + * @param String Benutzername + * @param String Kennwort + */ + public function setBasicAuthentication( $user, $password ) + { + $this->header['Authorization'] = 'Basic '.base64_encode($user.':'.$password); + } + + + + /** + * Erzeugt eine HTTP-Parameterstring mit allen Parametern. + * + * @param withPraefixQuestionMark Praefix mit Fragezeichen (fuer GET-Anfragen) + * @return String URL-Parameter + */ + public function getParameterString( $withPraefixQuestionMark=false ) + { + $parameterString = ''; + $parameter = $this->urlParameter + $this->requestParameter; + + if ( ! empty($parameter) ) + { + foreach( $this->requestParameter as $paramName => $paramValue ) + { + if ( strlen($parameterString) > 0) + $parameterString .= '&'; + elseif ( $withPraefixQuestionMark ) + $parameterString .= '?'; + + $parameterString .= urlencode($paramName) . '=' .urlencode($paramValue); + } + } + + return $parameterString; + } + + + /** + * Liefert die URL des Requests. + * + * @return String URL + */ + public function getUrl() + { + $location = $this->url['scheme']; + $location .= '://'; + $location .= $this->url['host']; + if ( $this->url['scheme'] == 'http' && $this->url['port'] != 80 || + $this->url['scheme'] == 'https' && $this->url['port'] != 443 ) + $location .= ':'.$this->url['port']; + $location .= $this->url['path']; + + $location .= $this->getParameterString(true); + + if ( isset($this->url['fragment']) ) + $location .= '#'.$this->url['fragment']; + + return $location; + } + + + /** + * Sendet eine Redirect-Anweisung mit der aktuellen URL an den Browser. + */ + public function sendRedirect() + { + $location = $this->getUrl(); + + header('Location: '.$location); + exit; + } + + + /** + * Führt einen HTTP-Request durch. + * + * @return boolean Erfolg der Anfrage. + */ + public function request() + { + $this->error = ''; + $this->status = ''; + + $errno = 0; + $errstr = ''; + + if ( empty($this->url['host']) ) + { + $this->error = "No hostname specified"; + return false; + } + + foreach( $this->header as $header_key=>$header_value ) + { + if ( is_numeric( $header_key ) ) + { + $dp = strpos($header_value,':'); + if ( $dp!==FALSE) + $this->header[substr($header_value,0,$dp)] = substr($header_value,$dp+1); + unset($this->header[$header_key]); + } + } + + $parameterString = $this->getParameterString(); + + if ( $this->method == 'POST' ) + { + $this->header['Content-Type' ] = 'application/x-www-form-urlencoded'; + $this->header['Content-Length'] = strlen($parameterString); + } + + // Accept-Header setzen, falls noch nicht vorhanden. + if ( !array_key_exists('Accept',$this->header) ) + $this->header['Accept'] = '*/*'; + + $this->responseHeader = array(); + + // RFC 1945 (Section 9.3) says: + // A user agent should never automatically redirect a request + // more than 5 times, since such redirections usually indicate an infinite loop. + for( $r=1; $r<=5; $r++ ) + { + $this->header['Host'] = $this->url['host']; + + // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig). + if ( $this->url['scheme'] == 'https' || $this->url['port'] == '443' ) + $prx_proto = 'ssl://'; // SSL + else + $prx_proto = 'tcp://'; // Default + + $fp = @fsockopen ($prx_proto.$this->url['host'],$this->url['port'], $errno, $errstr, 30); + + if ( !$fp || !is_resource($fp) ) + { + // Keine Verbindung zum Host moeglich. + $this->error = "Connection refused: '".$prx_proto.$this->url['host'].':'.$this->url['port']." - $errstr ($errno)"; + return false; + } + else + { + + $lb = "\r\n"; + $http_get = $this->url['path']; + + $request_header = array( $this->method.' '.$http_get.' HTTP/1.0'); + + foreach($this->header as $header_key=>$header_value) + $request_header[] = $header_key.': '.$header_value; + + $http_request = implode($lb,$request_header).$lb.$lb; + + if ( $this->method == 'GET') + if ( !empty($parameterString) ) + $http_get .= '?'.$parameterString; + + if ( $this->method == 'POST' ) + $http_request .= $parameterString; + + if (!is_resource($fp)) { + $this->error = 'Connection lost after connect: '.$prx_proto.$this->url['host'].':'.$this->url['port']; + return false; + } + fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden. + + // Jetzt erfolgt das Auslesen der HTTP-Antwort. + $isHeader = true; + + // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor + // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP + if (!is_resource($fp)) { + $this->error = 'Connection lost during transfer: '.$this->url['host'].':'.$this->url['port']; + return false; + } + elseif (!feof($fp)) { + $line = fgets($fp,1028); + $this->status = substr($line,9,3); + } + else + { + $this->error = 'Unexpected EOF while reading HTTP-Response'; + return false; + } + + $this->body = ''; + while (!feof($fp)) { + $line = fgets($fp,1028); + if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header. + { + $isHeader = false; + } + elseif( $isHeader ) + { + list($headerName,$headerValue) = explode(': ',$line) + array(1=>''); + $this->responseHeader[$headerName] = trim($headerValue); + } + else + { + $this->body .= $line; + } + } + fclose($fp); // Verbindung brav schlie�en. + + + // RFC 1945 (Section 6.1.1) schreibt + // "[...] However, applications must understand the class of any status code, as + // indicated by the first digit" + // Daher interessiert uns nur die erste Stelle des 3-stelligen HTTP-Status. + + // 301 Moved Permanently + // 302 Moved Temporarily + if ( $this->status == '301' || + $this->status == '302' ) + { + $location = @$this->responseHeader['Location']; + if ( empty($location) ) + { + $this->error = '301/302 Response without Location-header'; + return false; + } + + //Html::debug($this->url,"alte URL"); + //Html::debug($location,"NEUES REDIRECT AUF"); + $this->setURL($location); + continue; // Naechster Versuch mit umgeleiteter Adresse. + } + + // RFC 1945 (Section 6.1.1) schreibt + // "2xx: Success - The action was successfully received, understood, and accepted." + elseif ( substr($this->status,0,1) == '2' ) + { + return true; + } + elseif ( substr($this->status,0,1) == '4' ) + { + $this->error = 'Client Error: '.$this->status; + return false; + } + elseif ( substr($this->status,0,1) == '5' ) + { + $this->error = 'Server Error: '.$this->status; + return false; + } + else + { + $this->error = 'Unexpected HTTP-Status: '.$this->status. '; this is mostly a client error, sorry.'; + return false; + } + } + + $this->error = 'Too much redirects, infinite loop assumed. Exiting. Last URL: '.$http_get; + return false; + + } + + } + + + /** + * Aus dem HTTP-Header werden die vom Browser angeforderten Sprachen + * gelesen.<br> + * Es wird eine Liste von Sprachen erzeugt.<br> + * + * Beispiel: + * 'de_DE','de','en_GB','en' ... usw.<br> + * Wenn der Browser 'de_DE' anfordert, wird hier zusätzlich + * auch 'de' (als Fallback) ermittelt. + * + * @static + * @return Array + */ + public static function getLanguages() + { + global $HTTP_SERVER_VARS; + + $languages = array(); + $http_languages = @$HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE']; + foreach( explode(',',$http_languages) as $l ) + { + list($part) = explode(';',$l); // Priorit�ten ignorieren. + $languages[] = trim($part); + + // Aus "de_DE" das "de" extrahieren. + $languages[] = current(explode('_',str_replace('-','_',trim($part)))); + } + + return array_unique( $languages ); + } + + + /** + * Ermittelt die aktuelle HTTP-Adresse des Requests (inkl. Pfad, jedoch ohne Datei). + * + * @return String URL + */ + public static function getServer() + { + $https = getenv('HTTPS'); + + if ( $https ) + $server = 'https://'; + else + $server = 'http://'; + + $server .= getenv('SERVER_NAME').dirname(getenv('REQUEST_URI')); + + return $server; + } + + + + /** + * Server-Fehlermeldung anzeigen.<br> + * + * Erzeugt einen "HTTP 501 Internal Server Error". Zusaetzlich + * wird ein 'rollback' auf der Datenbank ausgefaehrt. + * + * @param String $message Eigener Hinweistext + */ + public static function serverError($message,$reason='') + { + if ( class_exists('Session')) + { + $db = Session::getDatabase(); + if ( is_object( $db ) ) + $db->rollback(); + } + + if ( class_exists('Logger')) + Logger::warn($message."\n".$reason); + + Http::sendStatus(501,'Internal Server Error',$message,$reason); + } + + + + /** + * Der Benutzer ist nicht autorisiert, eine Aktion auszufuehren. + * + * Diese Funktion erzeugt einen "HTTP 403 Not Authorized" und das + * Skript wird beendet. + * + * @param String $text Text + * @param String $message Eigener Hinweistext + */ + public static function notAuthorized($message='') + { + Logger::warn("Security warning: $message"); + Http::sendStatus(403,'Not authorized',$message); + } + + + + + + + /** + * Nichts gefunden. + * + * Diese Funktion erzeugt einen "HTTP 404 Not found" und das + * Skript wird beendet. + * + * @param String $text Text + * @param String $message Eigener Hinweistext + */ + public static function notFound($text,$message) + { + Http::sendStatus(404,'Not found',$message); + } + + + + /** + * Kein Inhalt. + * + * Die HTTP-Antwort stellt gegenüber dem Client klar, dass es keinen Inhalt gibt. + */ + public static function noContent() + { + header('HTTP/1.0 204 No Content'); + exit; + } + + + + /** + * Schickt einen HTTP-Status zum Client und beendet das Skript. + * + * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501) + * @param String $text HTTP-Meldung (Default: 'Internal Server Error') + * @param String $message Eigener Hinweistext (Default: leer) + * @param String $reason Technischer Grund (Default: leer) + */ + public static function sendStatus( $status=501,$text='Internal Server Error',$message='',$reason='' ) + { + if ( headers_sent() ) + { + echo "$status $text\n$message"; + exit; + } + + header('HTTP/1.0 '.intval($status).' '.$text); + + + $types = Http::getAccept(); + + if ( @$_REQUEST['output']=='json' || sizeof($types)==1 && in_array('application/json',$types) ) + { + header('Content-Type: application/json'); + require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT ); + $json = new JSON(); + echo $json->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) ); + } + elseif ( @$_REQUEST['output']=='xml' || sizeof($types)==1 && in_array('application/xml',$types) ) + { + header('Content-Type: application/xml'); + require_once( OR_SERVICECLASSES_DIR."XML.class.".PHP_EXT ); + $xml = new XML(); + $xml->root='error'; + echo $xml->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) ); + } + else + { + header('Content-Type: text/html'); + $message = htmlentities($message); + $reason = htmlentities($reason ); + $signature = OR_TITLE.' '.OR_VERSION.' '.getenv('SERVER_SOFTWARE'); + echo <<<HTML +<html> +<head><title>$status $text - OpenRat</title></head> +<body> +<h1>$text</h1> +<p>$message</p> +<pre>$reason</pre> +<hr> +<address>$signature</adddress> +</body> +</html> +HTML; + } + exit; + } + + + /** + * Liefert den Mime-Type, den der Browser (oder besser: HTTP-Client) wünscht. + * + * @return Array Mime-Typen, welche vom User-Agent akzeptiert werden. + */ + public static function getAccept() + { + $httpAccept = getenv('HTTP_ACCEPT'); + return $types = explode(',',$httpAccept); + } + + + + /** + * Liefert die IPv4-Adresse des Clients. Falls der Request durch einen Proxy kam, wird + * versucht, die echte IP-Adresse aus dem Anfrageheader zu ermitteln. + * + * @return Client-IPv4-Adresse + */ + public static function getClientIP() + { + $ip = ''; + + if ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ) + { + $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; + } + elseif ( isset($_SERVER["HTTP_CLIENT_IP"]) ) + { + $ip = $_SERVER["HTTP_CLIENT_IP"]; + } + elseif ( isset($_SERVER["REMOTE_ADDR"]) ) + { + $ip = $_SERVER["REMOTE_ADDR"]; + } + + return $ip; + } + + + + /** + * Ermittelt den TCP/IP-Port des Clients. + * Achtung, bei Proxy-Zugriffen kann dies der Port des Proxys sein. + * + * @return TCP/IP-Port + */ + public static function getClientPort() + { + $ip = ''; + + if ( isset($_SERVER["REMOTE_PORT"]) ) + { + $ip = $_SERVER["REMOTE_PORT"]; + } + + return $ip; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/JSON.class.php b/modules/util/JSON.class.php @@ -0,0 +1,809 @@ +<?php +/** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski <mike-json@teczno.com> + * @author Matt Knapp <mdknapp[at]gmail[dot]com> + * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> + * @author Jan Dankert + * @copyright 2005 Michal Migurski + * @version CVS: $Id$ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_INDENT', "\t"); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * <code> + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * </code> + */ +class JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON() + { + $this->use = SERVICES_JSON_LOOSE_TYPE; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + static $indentNr = 0; + + $indent = str_repeat(SERVICES_JSON_INDENT,$indentNr); + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2}), + @ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2}), + @ord($var{$c + 3}), + @ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2}), + @ord($var{$c + 3}), + @ord($var{$c + 4}), + @ord(@$var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $indentNr++; + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + $indentNr--; + + foreach($properties as $property) { + if(JSON::isError($property)) { + return $property; + } + } + + return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) ."\n$indent".'}'."\n$indent"; + } + + // treat it like a regular array + $indentNr++; + $elements = array_map(array($this, 'encode'), $var); + $indentNr--; + + foreach($elements as $element) { + if(JSON::isError($element)) { + return $element; + } + } + + return "\n$indent".'['."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $elements) . "\n$indent".']'."\n$indent"; + + case 'object': + $vars = get_object_vars($var); + + $indentNr++; + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + $indentNr--; + + foreach($properties as $property) { + if(JSON::isError($property)) { + return $property; + } + } + + return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) . "\n$indent".'}'."\n$indent"; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value ) + { + $encoded_value = $this->encode($value); + + if(JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'json_error' || + is_subclass_of($data, 'json_error'))) { + return true; + } + + return false; + } +} + + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class JSON_Error + { + function JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + + +?> diff --git a/modules/util/JSqueeze.class.php b/modules/util/JSqueeze.class.php @@ -0,0 +1,1064 @@ +<?php + +/* + * Copyright (C) 2016 Nicolas Grekas - p@tchwork.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the (at your option): + * Apache License v2.0 (see provided LICENCE.ASL20 file), or + * GNU General Public License v2.0 (see provided LICENCE.GPLv2 file). + */ + +//namespace Patchwork; + +/* +* +* This class shrinks Javascript code +* (a process called minification nowadays) +* +* Should work with most valid Javascript code, +* even when semi-colons are missing. +* +* Features: +* - Removes comments and white spaces. +* - Renames every local vars, typically to a single character. +* - Renames also global vars, methods and properties, but only if they +* are marked special by some naming convention. By default, special +* var names begin with one or more "$", or with a single "_". +* - Renames also local/global vars found in strings, +* but only if they are marked special. +* - Keep Microsoft's conditional comments. +* - Output is optimized for later HTTP compression. +* +* Notes: +* - Source code must be parse error free before processing. +* - In order to maximise later HTTP compression (deflate, gzip), +* new variables names are chosen by considering closures, +* variables' frequency and characters' frequency. +* - If you use with/eval then be careful. +* +* Bonus: +* - Replaces false/true by !1/!0 +* - Replaces new Array/Object by []/{} +* - Merges consecutive "var" declarations with commas +* - Merges consecutive concatened strings +* - Fix a bug in Safari's parser (http://forums.asp.net/thread/1585609.aspx) +* - Can replace optional semi-colons by line feeds, +* thus facilitating output debugging. +* - Keep important comments marked with /*!... +* - Treats three semi-colons ;;; like single-line comments +* (http://dean.edwards.name/packer/2/usage/#triple-semi-colon). +* - Fix special catch scope across browsers +* - Work around buggy-handling of named function expressions in IE<=8 +* +* TODO? +* - foo['bar'] => foo.bar +* - {'foo':'bar'} => {foo:'bar'} +* - Dead code removal (never used function) +* - Munge primitives: var WINDOW=window, etc. +*/ + +class JSqueeze +{ + const + + SPECIAL_VAR_PACKER = '(\$+[a-zA-Z_]|_[a-zA-Z0-9$])[a-zA-Z0-9_$]*'; + + public + + $charFreq; + + protected + + $strings, + $closures, + $str0, + $str1, + $argFreq, + $specialVarRx, + $keepImportantComments, + + $varRx = '(?:[a-zA-Z_$])[a-zA-Z0-9_$]*', + $reserved = array( + // Literals + 'true','false','null', + // ES6 + 'break','case','class','catch','const','continue','debugger','default','delete','do','else','export','extends','finally','for','function','if','import','in','instanceof','new','return','super','switch','this','throw','try','typeof','var','void','while','with','yield', + // Future + 'enum', + // Strict mode + 'implements','package','protected','static','let','interface','private','public', + // Module + 'await', + // Older standards + 'abstract','boolean','byte','char','double','final','float','goto','int','long','native','short','synchronized','throws','transient','volatile', + ); + + public function __construct() + { + $this->reserved = array_flip($this->reserved); + $this->charFreq = array_fill(0, 256, 0); + } + + /** + * Squeezes a JavaScript source code. + * + * Set $singleLine to false if you want optional + * semi-colons to be replaced by line feeds. + * + * Set $keepImportantComments to false if you want /*! comments to be removed. + * + * $specialVarRx defines the regular expression of special variables names + * for global vars, methods, properties and in string substitution. + * Set it to false if you don't want any. + * + * If the analysed javascript source contains a single line comment like + * this one, then the directive will overwrite $specialVarRx: + * + * // jsqueeze.specialVarRx = your_special_var_regexp_here + * + * Only the first directive is parsed, others are ignored. It is not possible + * to redefine $specialVarRx in the middle of the javascript source. + * + * Example: + * $parser = new JSqueeze; + * $squeezed_js = $parser->squeeze($fat_js); + */ + public function squeeze($code, $singleLine = true, $keepImportantComments = true, $specialVarRx = false) + { + $code = trim($code); + if ('' === $code) { + return ''; + } + + $this->argFreq = array(-1 => 0); + $this->specialVarRx = $specialVarRx; + $this->keepImportantComments = !!$keepImportantComments; + + if (preg_match("#//[ \t]*jsqueeze\.specialVarRx[ \t]*=[ \t]*([\"']?)(.*)\\1#i", $code, $key)) { + if (!$key[1]) { + $key[2] = trim($key[2]); + $key[1] = strtolower($key[2]); + $key[1] = $key[1] && $key[1] != 'false' && $key[1] != 'none' && $key[1] != 'off'; + } + + $this->specialVarRx = $key[1] ? $key[2] : false; + } + + // Remove capturing parentheses + $this->specialVarRx && $this->specialVarRx = preg_replace('/(?<!\\\\)((?:\\\\\\\\)*)\((?!\?)/', '(?:', $this->specialVarRx); + + false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n"); + false !== strpos($code, "\xC2\x85") && $code = str_replace("\xC2\x85", "\n", $code); // Next Line + false !== strpos($code, "\xE2\x80\xA8") && $code = str_replace("\xE2\x80\xA8", "\n", $code); // Line Separator + false !== strpos($code, "\xE2\x80\xA9") && $code = str_replace("\xE2\x80\xA9", "\n", $code); // Paragraph Separator + + list($code, $this->strings) = $this->extractStrings($code); + list($code, $this->closures) = $this->extractClosures($code); + + $key = "//''\"\"#0'"; // This crap has a wonderful property: it can not happen in any valid javascript, even in strings + $this->closures[$key] = &$code; + + $tree = array($key => array('parent' => false)); + $this->makeVars($code, $tree[$key], $key); + $this->renameVars($tree[$key], true); + + $code = substr($tree[$key]['code'], 1); + $code = preg_replace("'\breturn !'", 'return!', $code); + $code = preg_replace("'\}(?=(else|while)[^\$.a-zA-Z0-9_])'", "}\r", $code); + $code = str_replace(array_keys($this->strings), array_values($this->strings), $code); + + if ($singleLine) { + $code = strtr($code, "\n", ';'); + } else { + $code = str_replace("\n", ";\n", $code); + } + false !== strpos($code, "\r") && $code = strtr(trim($code), "\r", "\n"); + + // Cleanup memory + $this->charFreq = array_fill(0, 256, 0); + $this->strings = $this->closures = $this->argFreq = array(); + $this->str0 = $this->str1 = ''; + + return $code; + } + + protected function extractStrings($f) + { + if ($cc_on = false !== strpos($f, '@cc_on')) { + // Protect conditional comments from being removed + $f = str_replace('#', '##', $f); + $f = str_replace('/*@', '1#@', $f); + $f = preg_replace("'//@([^\n]+)'", '2#@$1@#3', $f); + $f = str_replace('@*/', '@#1', $f); + } + + $len = strlen($f); + $code = str_repeat(' ', $len); + $j = 0; + + $strings = array(); + $K = 0; + + $instr = false; + + $q = array( + "'", '"', + "'" => 0, + '"' => 0, + ); + + // Extract strings, removes comments + for ($i = 0; $i < $len; ++$i) { + if ($instr) { + if ('//' == $instr) { + if ("\n" == $f[$i]) { + $f[$i--] = ' '; + $instr = false; + } + } elseif ($f[$i] == $instr || ('/' == $f[$i] && "/'" == $instr)) { + if ('!' == $instr) { + } elseif ('*' == $instr) { + if ('/' == $f[$i + 1]) { + ++$i; + $instr = false; + } + } else { + if ("/'" == $instr) { + while (isset($f[$i + 1]) && false !== strpos('gmi', $f[$i + 1])) { + $s[] = $f[$i++]; + } + $s[] = $f[$i]; + } + + $instr = false; + } + } elseif ('*' == $instr) { + } elseif ('!' == $instr) { + if ('*' == $f[$i] && '/' == $f[$i + 1]) { + $s[] = "*/\r"; + ++$i; + $instr = false; + } elseif ("\n" == $f[$i]) { + $s[] = "\r"; + } else { + $s[] = $f[$i]; + } + } elseif ('\\' == $f[$i]) { + ++$i; + + if ("\n" != $f[$i]) { + isset($q[$f[$i]]) && ++$q[$f[$i]]; + $s[] = '\\'.$f[$i]; + } + } elseif ('[' == $f[$i] && "/'" == $instr) { + $instr = '/['; + $s[] = '['; + } elseif (']' == $f[$i] && '/[' == $instr) { + $instr = "/'"; + $s[] = ']'; + } elseif ("'" == $f[$i] || '"' == $f[$i]) { + ++$q[$f[$i]]; + $s[] = '\\'.$f[$i]; + } else { + $s[] = $f[$i]; + } + } else { + switch ($f[$i]) { + case ';': + // Remove triple semi-colon + if ($i > 0 && ';' == $f[$i - 1] && $i + 1 < $len && ';' == $f[$i + 1]) { + $f[$i] = $f[$i + 1] = '/'; + } else { + $code[++$j] = ';'; + break; + } + + case '/': + if ('*' == $f[$i + 1]) { + ++$i; + $instr = '*'; + + if ($this->keepImportantComments && '!' == $f[$i + 1]) { + ++$i; + // no break here + } else { + break; + } + } elseif ('/' == $f[$i + 1]) { + ++$i; + $instr = '//'; + break; + } else { + $a = $j && (' ' == $code[$j] || "\x7F" == $code[$j]) ? $code[$j - 1] : $code[$j]; + if (false !== strpos('-!%&;<=>~:^+|,()*?[{} ', $a) + || (false !== strpos('oenfd', $a) + && preg_match( + "'(?<![\$.a-zA-Z0-9_])(do|else|return|typeof|yield[ \x7F]?\*?)[ \x7F]?$'", + substr($code, $j - 7, 8) + )) + ) { + if (')' === $a && $j > 1) { + $a = 1; + $k = $j - (' ' == $code[$j] || "\x7F" == $code[$j]) - 1; + while ($k >= 0 && $a) { + if ('(' === $code[$k]) { + --$a; + } elseif (')' === $code[$k]) { + ++$a; + } + --$k; + } + if (!preg_match("'(?<![\$.a-zA-Z0-9_])(if|for|while)[ \x7F]?$'", substr($code, 0, $k + 1))) { + $code[++$j] = '/'; + break; + } + } + + $key = "//''\"\"".$K++.$instr = "/'"; + $a = $j; + $code .= $key; + while (isset($key[++$j - $a - 1])) { + $code[$j] = $key[$j - $a - 1]; + } + --$j; + isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); + $strings[$key] = array('/'); + $s = &$strings[$key]; + } else { + $code[++$j] = '/'; + } + + break; + } + + case "'": + case '"': + $instr = $f[$i]; + $key = "//''\"\"".$K++.('!' == $instr ? ']' : "'"); + $a = $j; + $code .= $key; + while (isset($key[++$j - $a - 1])) { + $code[$j] = $key[$j - $a - 1]; + } + --$j; + isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); + $strings[$key] = array(); + $s = &$strings[$key]; + '!' == $instr && $s[] = "\r/*!"; + + break; + + case "\n": + if ($j > 3) { + if (' ' == $code[$j] || "\x7F" == $code[$j]) { + --$j; + } + + $code[++$j] = + false !== strpos('kend+-', $code[$j - 1]) + && preg_match( + "'(?:\+\+|--|(?<![\$.a-zA-Z0-9_])(break|continue|return|yield[ \x7F]?\*?))[ \x7F]?$'", + substr($code, $j - 8, 9) + ) + ? ';' : "\x7F"; + + break; + } + + case "\t": $f[$i] = ' '; + case ' ': + if (!$j || ' ' == $code[$j] || "\x7F" == $code[$j]) { + break; + } + + default: + $code[++$j] = $f[$i]; + } + } + } + + isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); + unset($s); + + $code = substr($code, 0, $j + 1); + $cc_on && $this->restoreCc($code, false); + + // Protect wanted spaces and remove unwanted ones + $code = strtr($code, "\x7F", ' '); + $code = str_replace('- -', "-\x7F-", $code); + $code = str_replace('+ +', "+\x7F+", $code); + $code = preg_replace("'(\d)\s+\.\s*([a-zA-Z\$_[(])'", "$1\x7F.$2", $code); + $code = preg_replace("# ([-!%&;<=>~:.^+|,()*?[\]{}/']+)#", '$1', $code); + $code = preg_replace("#([-!%&;<=>~:.^+|,()*?[\]{}/]+) #", '$1', $code); + $cc_on && $code = preg_replace_callback("'//[^\'].*?@#3'", function ($m) { return strtr($m[0], ' ', "\x7F"); }, $code); + + // Replace new Array/Object by []/{} + false !== strpos($code, 'new Array') && $code = preg_replace("'new Array(?:\(\)|([;\])},:]))'", '[]$1', $code); + false !== strpos($code, 'new Object') && $code = preg_replace("'new Object(?:\(\)|([;\])},:]))'", '{}$1', $code); + + // Add missing semi-colons after curly braces + // This adds more semi-colons than strictly needed, + // but it seems that later gzipping is favorable to the repetition of "};" + $code = preg_replace("'\}(?![:,;.()\[\]}\|&]|(else|catch|finally|while)[^\$.a-zA-Z0-9_])'", '};', $code); + + // Tag possible empty instruction for easy detection + $code = preg_replace("'(?<![\$.a-zA-Z0-9_])if\('", '1#(', $code); + $code = preg_replace("'(?<![\$.a-zA-Z0-9_])for\('", '2#(', $code); + $code = preg_replace("'(?<![\$.a-zA-Z0-9_])while\('", '3#(', $code); + + $forPool = array(); + $instrPool = array(); + $s = 0; + + $f = array(); + $j = -1; + + // Remove as much semi-colon as possible + $len = strlen($code); + for ($i = 0; $i < $len; ++$i) { + switch ($code[$i]) { + case '(': + if ($j >= 0 && "\n" == $f[$j]) { + $f[$j] = ';'; + } + + ++$s; + + if ($i && '#' == $code[$i - 1]) { + $instrPool[$s - 1] = 1; + if ('2' == $code[$i - 2]) { + $forPool[$s] = 1; + } + } + + $f[++$j] = '('; + break; + + case ']': + case ')': + if ($i + 1 < $len && !isset($forPool[$s]) && !isset($instrPool[$s - 1]) && preg_match("'[a-zA-Z0-9_\$]'", $code[$i + 1])) { + $f[$j] .= $code[$i]; + $f[++$j] = "\n"; + } else { + $f[++$j] = $code[$i]; + } + + if (')' == $code[$i]) { + unset($forPool[$s]); + --$s; + } + + continue 2; + + case '}': + if ("\n" == $f[$j]) { + $f[$j] = '}'; + } else { + $f[++$j] = '}'; + } + break; + + case ';': + if (isset($forPool[$s]) || isset($instrPool[$s])) { + $f[++$j] = ';'; + } elseif ($j >= 0 && "\n" != $f[$j] && ';' != $f[$j]) { + $f[++$j] = "\n"; + } + + break; + + case '#': + switch ($f[$j]) { + case '1': $f[$j] = 'if'; break 2; + case '2': $f[$j] = 'for'; break 2; + case '3': $f[$j] = 'while'; break 2; + } + + case '['; + if ($j >= 0 && "\n" == $f[$j]) { + $f[$j] = ';'; + } + + default: $f[++$j] = $code[$i]; + } + + unset($instrPool[$s]); + } + + $f = implode('', $f); + $cc_on && $f = str_replace('@#3', "\r", $f); + + // Fix "else ;" empty instructions + $f = preg_replace("'(?<![\$.a-zA-Z0-9_])else\n'", "\n", $f); + + $r1 = array( // keywords with a direct object + 'case','delete','do','else','function','in','instanceof','of','break', + 'new','return','throw','typeof','var','void','yield','let','if', + 'const','get','set', + ); + + $r2 = array( // keywords with a subject + 'in','instanceof','of', + ); + + // Fix missing semi-colons + $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])".implode(')(?<!(?<![a-zA-Z0-9_\$])', $r1).') (?!('.implode('|', $r2).")(?![a-zA-Z0-9_\$]))'", "\n", $f); + $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])do)(?<!(?<![a-zA-Z0-9_\$])else) if\('", "\nif(", $f); + $f = preg_replace("'(?<=--|\+\+)(?<![a-zA-Z0-9_\$])(".implode('|', $r1).")(?![a-zA-Z0-9_\$])'", "\n$1", $f); + $f = preg_replace("'(?<![a-zA-Z0-9_\$])for\neach\('", 'for each(', $f); + $f = preg_replace("'(?<![a-zA-Z0-9_\$])\n(".implode('|', $r2).")(?![a-zA-Z0-9_\$])'", '$1', $f); + + // Merge strings + if ($q["'"] > $q['"']) { + $q = array($q[1], $q[0]); + } + $f = preg_replace("#//''\"\"[0-9]+'#", $q[0].'$0'.$q[0], $f); + strpos($f, $q[0].'+'.$q[0]) && $f = str_replace($q[0].'+'.$q[0], '', $f); + $len = count($strings); + foreach ($strings as $r1 => &$r2) { + $r2 = "/'" == substr($r1, -2) + ? str_replace(array("\\'", '\\"'), array("'", '"'), $r2) + : str_replace('\\'.$q[1], $q[1], $r2); + } + + // Restore wanted spaces + $f = strtr($f, "\x7F", ' '); + + return array($f, $strings); + } + + protected function extractClosures($code) + { + $code = ';'.$code; + + $this->argFreq[-1] += substr_count($code, '}catch('); + + if ($this->argFreq[-1]) { + // Special catch scope handling + + // FIXME: this implementation doesn't work with nested catch scopes who need + // access to their parent's caught variable (but who needs that?). + + $f = preg_split("@}catch\(({$this->varRx})@", $code, -1, PREG_SPLIT_DELIM_CAPTURE); + + $code = 'catch$scope$var'.mt_rand(); + $this->specialVarRx = $this->specialVarRx ? '(?:'.$this->specialVarRx.'|'.preg_quote($code).')' : preg_quote($code); + $i = count($f) - 1; + + while ($i) { + $c = 1; + $j = 0; + $l = strlen($f[$i]); + + while ($c && $j < $l) { + $s = $f[$i][$j++]; + $c += '(' == $s ? 1 : (')' == $s ? -1 : 0); + } + + if (!$c) { + do { + $s = $f[$i][$j++]; + $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0); + } while ($c && $j < $l); + } + + $c = preg_quote($f[$i - 1], '#'); + $f[$i - 2] .= '}catch('.preg_replace("#([.,{]?)(?<![a-zA-Z0-9_\$@]){$c}\\b#", '$1'.$code, $f[$i - 1].substr($f[$i], 0, $j)).substr($f[$i], $j); + + unset($f[$i--], $f[$i--]); + } + + $code = $f[0]; + } + + $f = preg_split("'(?<![a-zA-Z0-9_\$])((?:function[ (]|get |set ).*?\{)'", $code, -1, PREG_SPLIT_DELIM_CAPTURE); + $i = count($f) - 1; + $closures = array(); + + while ($i) { + $c = 1; + $j = 0; + $l = strlen($f[$i]); + + while ($c && $j < $l) { + $s = $f[$i][$j++]; + $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0); + } + + switch (substr($f[$i - 2], -1)) { + default: + if (false !== $c = strpos($f[$i - 1], ' ', 8)) { + break; + } + case false: case "\n": case ';': case '{': case '}': case ')': case ']': + $c = strpos($f[$i - 1], '(', 4); + } + + $l = "//''\"\"#$i'"; + $code = substr($f[$i - 1], $c); + $closures[$l] = $code.substr($f[$i], 0, $j); + $f[$i - 2] .= substr($f[$i - 1], 0, $c).$l.substr($f[$i], $j); + + if ('(){' !== $code) { + $j = substr_count($code, ','); + do { + isset($this->argFreq[$j]) ? ++$this->argFreq[$j] : $this->argFreq[$j] = 1; + } while ($j--); + } + + $i -= 2; + } + + return array($f[0], $closures); + } + + protected function makeVars($closure, &$tree, $key) + { + $tree['code'] = &$closure; + $tree['nfe'] = false; + $tree['used'] = array(); + $tree['local'] = array(); + + // Replace multiple "var" declarations by a single one + $closure = preg_replace_callback("'(?<=[\n\{\}])var [^\n\{\};]+(?:\nvar [^\n\{\};]+)+'", array($this, 'mergeVarDeclarations'), $closure); + + // Get all local vars (functions, arguments and "var" prefixed) + + $vars = &$tree['local']; + + if (preg_match("'^( [^(]*)?\((.*?)\)\{'", $closure, $v)) { + if ($v[1]) { + $vars[$tree['nfe'] = substr($v[1], 1)] = -1; + $tree['parent']['local'][';'.$key] = &$vars[$tree['nfe']]; + } + + if ($v[2]) { + $i = 0; + $v = explode(',', $v[2]); + foreach ($v as $w) { + $vars[$w] = $this->argFreq[$i++] - 1; // Give a bonus to argument variables + } + } + } + + $v = preg_split("'(?<![\$.a-zA-Z0-9_])var '", $closure); + if ($i = count($v) - 1) { + $w = array(); + + while ($i) { + $j = $c = 0; + $l = strlen($v[$i]); + + while ($j < $l) { + switch ($v[$i][$j]) { + case '(': case '[': case '{': + ++$c; + break; + + case ')': case ']': case '}': + if ($c-- <= 0) { + break 2; + } + break; + + case ';': case "\n": + if (!$c) { + break 2; + } + + default: + $c || $w[] = $v[$i][$j]; + } + + ++$j; + } + + $w[] = ','; + --$i; + } + + $v = explode(',', implode('', $w)); + foreach ($v as $w) { + if (preg_match("'^{$this->varRx}'", $w, $v)) { + isset($vars[$v[0]]) || $vars[$v[0]] = 0; + } + } + } + + if (preg_match_all("@function ({$this->varRx})//''\"\"#@", $closure, $v)) { + foreach ($v[1] as $w) { + isset($vars[$w]) || $vars[$w] = 0; + } + } + + if ($this->argFreq[-1] && preg_match_all("@}catch\(({$this->varRx})@", $closure, $v)) { + $v[0] = array(); + foreach ($v[1] as $w) { + isset($v[0][$w]) ? ++$v[0][$w] : $v[0][$w] = 1; + } + foreach ($v[0] as $w => $v) { + $vars[$w] = $this->argFreq[-1] - $v; + } + } + + // Get all used vars, local and non-local + + $vars = &$tree['used']; + + if (preg_match_all("#([.,{]?(?:[gs]et )?)(?<![a-zA-Z0-9_\$])({$this->varRx})(:?)#", $closure, $w, PREG_SET_ORDER)) { + foreach ($w as $k) { + if (isset($k[1][0]) && (',' === $k[1][0] || '{' === $k[1][0])) { + if (':' === $k[3]) { + $k = '.'.$k[2]; + } elseif ('get ' === substr($k[1], 1, 4) || 'set ' === substr($k[1], 1, 4)) { + ++$this->charFreq[ord($k[1][1])]; // "g" or "s" + ++$this->charFreq[101]; // "e" + ++$this->charFreq[116]; // "t" + $k = '.'.$k[2]; + } else { + $k = $k[2]; + } + } else { + $k = $k[1].$k[2]; + } + + isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1; + } + } + + if (preg_match_all("#//''\"\"[0-9]+(?:['!]|/')#", $closure, $w)) { + foreach ($w[0] as $a) { + $v = "'" === substr($a, -1) && "/'" !== substr($a, -2) && $this->specialVarRx + ? preg_split("#([.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?)#", $this->strings[$a], -1, PREG_SPLIT_DELIM_CAPTURE) + : array($this->strings[$a]); + $a = count($v); + + for ($i = 0; $i < $a; ++$i) { + $k = $v[$i]; + + if (1 === $i % 2) { + if (',' === $k[0] || '{' === $k[0]) { + if (':' === substr($k, -1)) { + $k = '.'.substr($k, 1, -1); + } elseif ('get ' === substr($k, 1, 4) || 'set ' === substr($k, 1, 4)) { + ++$this->charFreq[ord($k[1])]; // "g" or "s" + ++$this->charFreq[101]; // "e" + ++$this->charFreq[116]; // "t" + $k = '.'.substr($k, 5); + } else { + $k = substr($k, 1); + } + } elseif (':' === substr($k, -1)) { + $k = substr($k, 0, -1); + } + + $w = &$tree; + + while (isset($w['parent']) && !(isset($w['used'][$k]) || isset($w['local'][$k]))) { + $w = &$w['parent']; + } + + (isset($w['used'][$k]) || isset($w['local'][$k])) && (isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1); + + unset($w); + } + + if (0 === $i % 2 || !isset($vars[$k])) { + foreach (count_chars($v[$i], 1) as $k => $w) { + $this->charFreq[$k] += $w; + } + } + } + } + } + + // Propagate the usage number to parents + + foreach ($vars as $w => $a) { + $k = &$tree; + $chain = array(); + do { + $vars = &$k['local']; + $chain[] = &$k; + if (isset($vars[$w])) { + unset($k['used'][$w]); + if (isset($vars[$w])) { + $vars[$w] += $a; + } else { + $vars[$w] = $a; + } + $a = false; + break; + } + } while ($k['parent'] && $k = &$k['parent']); + + if ($a && !$k['parent']) { + if (isset($vars[$w])) { + $vars[$w] += $a; + } else { + $vars[$w] = $a; + } + } + + if (isset($tree['used'][$w]) && isset($vars[$w])) { + foreach ($chain as &$b) { + isset($b['local'][$w]) || $b['used'][$w] = &$vars[$w]; + } + } + } + + // Analyse childs + + $tree['childs'] = array(); + $vars = &$tree['childs']; + + if (preg_match_all("@//''\"\"#[0-9]+'@", $closure, $w)) { + foreach ($w[0] as $a) { + $vars[$a] = array('parent' => &$tree); + $this->makeVars($this->closures[$a], $vars[$a], $a); + } + } + } + + protected function mergeVarDeclarations($m) + { + return str_replace("\nvar ", ',', $m[0]); + } + + protected function renameVars(&$tree, $root) + { + if ($root) { + $tree['local'] += $tree['used']; + $tree['used'] = array(); + + foreach ($tree['local'] as $k => $v) { + if ('.' == $k[0]) { + $k = substr($k, 1); + } + + if ('true' === $k) { + $this->charFreq[48] += $v; + } elseif ('false' === $k) { + $this->charFreq[49] += $v; + } elseif (!$this->specialVarRx || !preg_match("#^{$this->specialVarRx}$#", $k)) { + foreach (count_chars($k, 1) as $k => $w) { + $this->charFreq[$k] += $w * $v; + } + } elseif (2 == strlen($k)) { + $tree['used'][] = $k[1]; + } + } + + $this->charFreq = $this->rsort($this->charFreq); + + $this->str0 = ''; + $this->str1 = ''; + + foreach ($this->charFreq as $k => $v) { + if (!$v) { + break; + } + + $v = chr($k); + + if ((64 < $k && $k < 91) || (96 < $k && $k < 123)) { // A-Z a-z + $this->str0 .= $v; + $this->str1 .= $v; + } elseif (47 < $k && $k < 58) { // 0-9 + $this->str1 .= $v; + } + } + + if ('' === $this->str0) { + $this->str0 = 'claspemitdbfrugnjvhowkxqyzCLASPEMITDBFRUGNJVHOWKXQYZ'; + $this->str1 = $this->str0.'0123456789'; + } + + foreach ($tree['local'] as $var => $root) { + if ('.' != substr($var, 0, 1) && isset($tree['local'][".{$var}"])) { + $tree['local'][$var] += $tree['local'][".{$var}"]; + } + } + + foreach ($tree['local'] as $var => $root) { + if ('.' == substr($var, 0, 1) && isset($tree['local'][substr($var, 1)])) { + $tree['local'][$var] = $tree['local'][substr($var, 1)]; + } + } + + $tree['local'] = $this->rsort($tree['local']); + + foreach ($tree['local'] as $var => $root) { + switch (substr($var, 0, 1)) { + case '.': + if (!isset($tree['local'][substr($var, 1)])) { + $tree['local'][$var] = '#'.($this->specialVarRx && 3 < strlen($var) && preg_match("'^\.{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : substr($var, 1)); + } + break; + + case ';': $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree); + case '#': break; + + default: + $root = $this->specialVarRx && 2 < strlen($var) && preg_match("'^{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : $var; + $tree['local'][$var] = $root; + if (isset($tree['local'][".{$var}"])) { + $tree['local'][".{$var}"] = '#'.$root; + } + } + } + + foreach ($tree['local'] as $var => $root) { + $tree['local'][$var] = preg_replace("'^#'", '.', $tree['local'][$var]); + } + } else { + $tree['local'] = $this->rsort($tree['local']); + if (false !== $tree['nfe']) { + $tree['used'][] = $tree['local'][$tree['nfe']]; + } + + foreach ($tree['local'] as $var => $root) { + if ($tree['nfe'] !== $var) { + $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree); + } + } + } + + $this->local_tree = &$tree['local']; + $this->used_tree = &$tree['used']; + + $tree['code'] = preg_replace_callback("#[.,{ ]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->varRx}:?#", array($this, 'getNewName'), $tree['code']); + + if ($this->specialVarRx && preg_match_all("#//''\"\"[0-9]+'#", $tree['code'], $b)) { + foreach ($b[0] as $a) { + $this->strings[$a] = preg_replace_callback( + "#[.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?#", + array($this, 'getNewName'), + $this->strings[$a] + ); + } + } + + foreach ($tree['childs'] as $a => &$b) { + $this->renameVars($b, false); + $tree['code'] = str_replace($a, $b['code'], $tree['code']); + unset($tree['childs'][$a]); + } + } + + protected function getNewName($m) + { + $m = $m[0]; + + $pre = '.' === $m[0] ? '.' : ''; + $post = ''; + + if (',' === $m[0] || '{' === $m[0] || ' ' === $m[0]) { + $pre = $m[0]; + + if (':' === substr($m, -1)) { + $post = ':'; + $m = (' ' !== $m[0] ? '.' : '').substr($m, 1, -1); + } elseif ('get ' === substr($m, 1, 4) || 'set ' === substr($m, 1, 4)) { + $pre .= substr($m, 1, 4); + $m = '.'.substr($m, 5); + } else { + $m = substr($m, 1); + } + } elseif (':' === substr($m, -1)) { + $post = ':'; + $m = substr($m, 0, -1); + } + + $post = (isset($this->reserved[$m]) + ? ('true' === $m ? '!0' : ('false' === $m ? '!1' : $m)) + : ( + isset($this->local_tree[$m]) + ? $this->local_tree[$m] + : ( + isset($this->used_tree[$m]) + ? $this->used_tree[$m] + : $m + ) + ) + ).$post; + + return '' === $post ? '' : ($pre.('.' === $post[0] ? substr($post, 1) : $post)); + } + + protected function getNextName(&$tree = array(), &$counter = false) + { + if (false === $counter) { + $counter = &$tree['counter']; + isset($counter) || $counter = -1; + $exclude = array_flip($tree['used']); + } else { + $exclude = $tree; + } + + ++$counter; + + $len0 = strlen($this->str0); + $len1 = strlen($this->str0); + + $name = $this->str0[$counter % $len0]; + + $i = intval($counter / $len0) - 1; + while ($i >= 0) { + $name .= $this->str1[ $i % $len1 ]; + $i = intval($i / $len1) - 1; + } + + return !(isset($this->reserved[$name]) || isset($exclude[$name])) ? $name : $this->getNextName($exclude, $counter); + } + + protected function restoreCc(&$s, $lf = true) + { + $lf && $s = str_replace('@#3', '', $s); + + $s = str_replace('@#1', '@*/', $s); + $s = str_replace('2#@', '//@', $s); + $s = str_replace('1#@', '/*@', $s); + $s = str_replace('##', '#', $s); + } + + private function rsort($array) + { + if (!$array) { + return $array; + } + + $i = 0; + $tuples = array(); + foreach ($array as $k => &$v) { + $tuples[] = array(++$i, $k, &$v); + } + + usort($tuples, function ($a, $b) { + if ($b[2] > $a[2]) { + return 1; + } + if ($b[2] < $a[2]) { + return -1; + } + if ($b[0] > $a[0]) { + return -1; + } + if ($b[0] < $a[0]) { + return 1; + } + + return 0; + }); + + $array = array(); + + foreach ($tuples as $t) { + $array[$t[1]] = &$t[2]; + } + + return $array; + } +} +?>+ \ No newline at end of file diff --git a/modules/util/Ldap.class.php b/modules/util/Ldap.class.php @@ -0,0 +1,182 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Bereitstellen von LDAP-Funktionen. + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Ldap +{ + var $connection; + var $timeout; + var $aliases; + + + /** + * + */ + function Ldap() + { + global $conf; + + $this->timeout = intval($conf['ldap']['search']['timeout']); + + if ( $conf['ldap']['search']['aliases'] ) + $this->aliases = LDAP_DEREF_ALWAYS; + else + $this->aliases = LDAP_DEREF_NEVER; + } + + + + /** + * Verbindung �ffnen. + */ + function connect() + { + global $conf; + + $ldapHost = $conf['ldap']['host']; + $ldapPort = $conf['ldap']['port']; + + // Verbindung zum LDAP-Server herstellen + $this->connection = @ldap_connect( $ldapHost,$ldapPort ); + + // siehe http://bugs.php.net/bug.php?id=15637 + // Unter bestimmten Bedingungen wird trotz nicht erreichbarem LDAP-Server eine PHP-Resource + // zurueck gegeben. Dann erscheint zwar keine Fehlermeldung, aber zumindestens misslingt + // der nachfolgende Bind-Befehl. + if ( !is_resource($this->connection) || $this->connection === false ) + { + Logger::error( "connect to ldap server '$ldapHost:$ldapPort' failed" ); + // Abbruch, wenn LDAP-Server nicht erreichbar + die( "Connection failed to $ldapHost:$ldapPort (".ldap_errno().'/'.ldap_error().'). Please contact your administrator.' ); + } + + // Protokollversion setzen. + $j = ldap_set_option( $this->connection, LDAP_OPT_PROTOCOL_VERSION,intval($conf['ldap']['protocol']) ); + if ( ! $j ) + die( 'LDAP error while setting protocol version'.ldap_errno().'/'.ldap_error().')' ); + + } + + + + /** + * Ein Binding auf den LDAP-Server durchf�hren. + */ + function bind( $user,$pw ) + { + return @ldap_bind( $this->connection,$user,$pw); + } + + + + /** + * Ein Binding auf den LDAP-Server durchf�hren. + */ + function bindAnonymous() + { + return @ldap_bind( $this->connection ); + } + + + + /** + * Das Bindung wird entfernt. + */ + function unbind() + { + ldap_unbind( $this->connection ); + } + + + + /** + * Eine Suche auf den LDAP-Server durchf�hren. + */ + function searchUser( $username ) + { + global $conf; + + $techUser = $conf['ldap']['search']['user']; + $techPass = $conf['ldap']['search']['password']; + + if ( $conf['ldap']['search']['anonymous'] ) + $this->bindAnonymous(); + else + $this->bind( $techUser, $techPass ); + + $dn = $conf['ldap']['search']['basedn']; + $filter = $conf['ldap']['search']['filter']; + $filter = str_replace('{user}', $username, $filter); + + $s = @ldap_search( $this->connection,$dn,$filter,array(),0,1,$this->timeout,$this->aliases ); + + if ( ! is_resource($s) ) + return null; + + $dn = @ldap_get_dn($this->connection, ldap_first_entry($this->connection,$s) ); + + return $dn; + } + + + + /** + * Ein Binding auf den LDAP-Server durchf�hren. + */ + function searchAttribute( $filter,$attr ) + { + global $conf; + + $timeout = intval($conf['ldap']['search']['timeout']); + + if ( $conf['ldap']['search']['aliases'] ) + $aliases = LDAP_DEREF_ALWAYS; + else + $aliases = LDAP_DEREF_NEVER; + + + $base_dn = $conf['ldap']['search']['basedn']; + $s = ldap_search( $this->connection,$base_dn,$filter,array(),0,0,$this->timeout,$this->aliases ); + $ergebnisse = ldap_get_entries($this->connection,$s); + + $liste = array(); +// Html::debug($ergebnisse); + for( $i=0; $i<=$ergebnisse['count']-1; $i++ ) + $liste[] = $ergebnisse[$i][$attr][0]; + + return $liste; + } + + + + /** + * Verbindung schlie�en. + */ + function close() + { + // Verbindung zum LDAP-Server brav beenden + ldap_close( $this->connection ); + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Less.php b/modules/util/Less.php @@ -0,0 +1,10524 @@ +<?php + +/** + * Class for parsing and compiling less files into css + * + * @package Less + * @subpackage parser + * + */ +class Less_Parser{ + + + /** + * Default parser options + */ + public static $default_options = array( + 'compress' => false, // option - whether to compress + 'strictUnits' => false, // whether units need to evaluate correctly + 'strictMath' => false, // whether math has to be within parenthesis + 'relativeUrls' => true, // option - whether to adjust URL's to be relative + 'urlArgs' => '', // whether to add args into url tokens + 'numPrecision' => 8, + + 'import_dirs' => array(), + 'import_callback' => null, + 'cache_dir' => null, + 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback'; + 'cache_callback_get' => null, + 'cache_callback_set' => null, + + 'sourceMap' => false, // whether to output a source map + 'sourceMapBasepath' => null, + 'sourceMapWriteTo' => null, + 'sourceMapURL' => null, + + 'indentation' => ' ', + + 'plugins' => array(), + + ); + + public static $options = array(); + + + private $input; // Less input string + private $input_len; // input string length + private $pos; // current index in `input` + private $saveStack = array(); // holds state for backtracking + private $furthest; + private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding + + /** + * @var Less_Environment + */ + private $env; + + protected $rules = array(); + + private static $imports = array(); + + public static $has_extends = false; + + public static $next_id = 0; + + /** + * Filename to contents of all parsed the files + * + * @var array + */ + public static $contentsMap = array(); + + + /** + * @param Less_Environment|array|null $env + */ + public function __construct( $env = null ){ + + // Top parser on an import tree must be sure there is one "env" + // which will then be passed around by reference. + if( $env instanceof Less_Environment ){ + $this->env = $env; + }else{ + $this->SetOptions(Less_Parser::$default_options); + $this->Reset( $env ); + } + + // mbstring.func_overload > 1 bugfix + // The encoding value must be set for each source file, + // therefore, to conserve resources and improve the speed of this design is taken here + if (ini_get('mbstring.func_overload')) { + $this->mb_internal_encoding = ini_get('mbstring.internal_encoding'); + @ini_set('mbstring.internal_encoding', 'ascii'); + } + + } + + + /** + * Reset the parser state completely + * + */ + public function Reset( $options = null ){ + $this->rules = array(); + self::$imports = array(); + self::$has_extends = false; + self::$imports = array(); + self::$contentsMap = array(); + + $this->env = new Less_Environment($options); + $this->env->Init(); + + //set new options + if( is_array($options) ){ + $this->SetOptions(Less_Parser::$default_options); + $this->SetOptions($options); + } + } + + /** + * Set one or more compiler options + * options: import_dirs, cache_dir, cache_method + * + */ + public function SetOptions( $options ){ + foreach($options as $option => $value){ + $this->SetOption($option,$value); + } + } + + /** + * Set one compiler option + * + */ + public function SetOption($option,$value){ + + switch($option){ + + case 'import_dirs': + $this->SetImportDirs($value); + return; + + case 'cache_dir': + if( is_string($value) ){ + Less_Cache::SetCacheDir($value); + Less_Cache::CheckCacheDir(); + } + return; + } + + Less_Parser::$options[$option] = $value; + } + + /** + * Registers a new custom function + * + * @param string $name function name + * @param callable $callback callback + */ + public function registerFunction($name, $callback) { + $this->env->functions[$name] = $callback; + } + + /** + * Removed an already registered function + * + * @param string $name function name + */ + public function unregisterFunction($name) { + if( isset($this->env->functions[$name]) ) + unset($this->env->functions[$name]); + } + + + /** + * Get the current css buffer + * + * @return string + */ + public function getCss(){ + + $precision = ini_get('precision'); + @ini_set('precision',16); + $locale = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, "C"); + + try { + + $root = new Less_Tree_Ruleset(array(), $this->rules ); + $root->root = true; + $root->firstRoot = true; + + + $this->PreVisitors($root); + + self::$has_extends = false; + $evaldRoot = $root->compile($this->env); + + + + $this->PostVisitors($evaldRoot); + + if( Less_Parser::$options['sourceMap'] ){ + $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options ); + // will also save file + // FIXME: should happen somewhere else? + $css = $generator->generateCSS(); + }else{ + $css = $evaldRoot->toCSS(); + } + + if( Less_Parser::$options['compress'] ){ + $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css); + } + + } catch (Exception $exc) { + // Intentional fall-through so we can reset environment + } + + //reset php settings + @ini_set('precision',$precision); + setlocale(LC_NUMERIC, $locale); + + // If you previously defined $this->mb_internal_encoding + // is required to return the encoding as it was before + if ($this->mb_internal_encoding != '') { + @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding); + $this->mb_internal_encoding = ''; + } + + // Rethrow exception after we handled resetting the environment + if (!empty($exc)) { + throw $exc; + } + + + + return $css; + } + + /** + * Run pre-compile visitors + * + */ + private function PreVisitors($root){ + + if( Less_Parser::$options['plugins'] ){ + foreach(Less_Parser::$options['plugins'] as $plugin){ + if( !empty($plugin->isPreEvalVisitor) ){ + $plugin->run($root); + } + } + } + } + + + /** + * Run post-compile visitors + * + */ + private function PostVisitors($evaldRoot){ + + $visitors = array(); + $visitors[] = new Less_Visitor_joinSelector(); + if( self::$has_extends ){ + $visitors[] = new Less_Visitor_processExtends(); + } + $visitors[] = new Less_Visitor_toCSS(); + + + if( Less_Parser::$options['plugins'] ){ + foreach(Less_Parser::$options['plugins'] as $plugin){ + if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){ + continue; + } + + if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){ + array_unshift( $visitors, $plugin); + }else{ + $visitors[] = $plugin; + } + } + } + + + for($i = 0; $i < count($visitors); $i++ ){ + $visitors[$i]->run($evaldRoot); + } + + } + + + /** + * Parse a Less string into css + * + * @param string $str The string to convert + * @param string $uri_root The url of the file + * @return Less_Tree_Ruleset|Less_Parser + */ + public function parse( $str, $file_uri = null ){ + + if( !$file_uri ){ + $uri_root = ''; + $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less'; + }else{ + $file_uri = self::WinPath($file_uri); + $filename = $file_uri; + $uri_root = dirname($file_uri); + } + + $previousFileInfo = $this->env->currentFileInfo; + $uri_root = self::WinPath($uri_root); + $this->SetFileInfo($filename, $uri_root); + + $this->input = $str; + $this->_parse(); + + if( $previousFileInfo ){ + $this->env->currentFileInfo = $previousFileInfo; + } + + return $this; + } + + + /** + * Parse a Less string from a given file + * + * @throws Less_Exception_Parser + * @param string $filename The file to parse + * @param string $uri_root The url of the file + * @param bool $returnRoot Indicates whether the return value should be a css string a root node + * @return Less_Tree_Ruleset|Less_Parser + */ + public function parseFile( $filename, $uri_root = '', $returnRoot = false){ + + if( !file_exists($filename) ){ + $this->Error(sprintf('File `%s` not found.', $filename)); + } + + + // fix uri_root? + // Instead of The mixture of file path for the first argument and directory path for the second argument has bee + if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){ + $uri_root = dirname($uri_root); + } + + + $previousFileInfo = $this->env->currentFileInfo; + + + if( $filename ){ + $filename = self::WinPath(realpath($filename)); + } + $uri_root = self::WinPath($uri_root); + + $this->SetFileInfo($filename, $uri_root); + + self::AddParsedFile($filename); + + if( $returnRoot ){ + $rules = $this->GetRules( $filename ); + $return = new Less_Tree_Ruleset(array(), $rules ); + }else{ + $this->_parse( $filename ); + $return = $this; + } + + if( $previousFileInfo ){ + $this->env->currentFileInfo = $previousFileInfo; + } + + return $return; + } + + + /** + * Allows a user to set variables values + * @param array $vars + * @return Less_Parser + */ + public function ModifyVars( $vars ){ + + $this->input = Less_Parser::serializeVars( $vars ); + $this->_parse(); + + return $this; + } + + + /** + * @param string $filename + */ + public function SetFileInfo( $filename, $uri_root = ''){ + + $filename = Less_Environment::normalizePath($filename); + $dirname = preg_replace('/[^\/\\\\]*$/','',$filename); + + if( !empty($uri_root) ){ + $uri_root = rtrim($uri_root,'/').'/'; + } + + $currentFileInfo = array(); + + //entry info + if( isset($this->env->currentFileInfo) ){ + $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath']; + $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri']; + $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath']; + + }else{ + $currentFileInfo['entryPath'] = $dirname; + $currentFileInfo['entryUri'] = $uri_root; + $currentFileInfo['rootpath'] = $dirname; + } + + $currentFileInfo['currentDirectory'] = $dirname; + $currentFileInfo['currentUri'] = $uri_root.basename($filename); + $currentFileInfo['filename'] = $filename; + $currentFileInfo['uri_root'] = $uri_root; + + + //inherit reference + if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){ + $currentFileInfo['reference'] = true; + } + + $this->env->currentFileInfo = $currentFileInfo; + } + + + /** + * @deprecated 1.5.1.2 + * + */ + public function SetCacheDir( $dir ){ + + if( !file_exists($dir) ){ + if( mkdir($dir) ){ + return true; + } + throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir); + + }elseif( !is_dir($dir) ){ + throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir); + + }elseif( !is_writable($dir) ){ + throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir); + + }else{ + $dir = self::WinPath($dir); + Less_Cache::$cache_dir = rtrim($dir,'/').'/'; + return true; + } + } + + + /** + * Set a list of directories or callbacks the parser should use for determining import paths + * + * @param array $dirs + */ + public function SetImportDirs( $dirs ){ + Less_Parser::$options['import_dirs'] = array(); + + foreach($dirs as $path => $uri_root){ + + $path = self::WinPath($path); + if( !empty($path) ){ + $path = rtrim($path,'/').'/'; + } + + if ( !is_callable($uri_root) ){ + $uri_root = self::WinPath($uri_root); + if( !empty($uri_root) ){ + $uri_root = rtrim($uri_root,'/').'/'; + } + } + + Less_Parser::$options['import_dirs'][$path] = $uri_root; + } + } + + /** + * @param string $file_path + */ + private function _parse( $file_path = null ){ + $this->rules = array_merge($this->rules, $this->GetRules( $file_path )); + } + + + /** + * Return the results of parsePrimary for $file_path + * Use cache and save cached results if possible + * + * @param string|null $file_path + */ + private function GetRules( $file_path ){ + + $this->SetInput($file_path); + + $cache_file = $this->CacheFile( $file_path ); + if( $cache_file ){ + if( Less_Parser::$options['cache_method'] == 'callback' ){ + if( is_callable(Less_Parser::$options['cache_callback_get']) ){ + $cache = call_user_func_array( + Less_Parser::$options['cache_callback_get'], + array($this, $file_path, $cache_file) + ); + + if( $cache ){ + $this->UnsetInput(); + return $cache; + } + } + + }elseif( file_exists($cache_file) ){ + switch(Less_Parser::$options['cache_method']){ + + // Using serialize + // Faster but uses more memory + case 'serialize': + $cache = unserialize(file_get_contents($cache_file)); + if( $cache ){ + touch($cache_file); + $this->UnsetInput(); + return $cache; + } + break; + + + // Using generated php code + case 'var_export': + case 'php': + $this->UnsetInput(); + return include($cache_file); + } + } + } + + $rules = $this->parsePrimary(); + + if( $this->pos < $this->input_len ){ + throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo); + } + + $this->UnsetInput(); + + + //save the cache + if( $cache_file ){ + if( Less_Parser::$options['cache_method'] == 'callback' ){ + if( is_callable(Less_Parser::$options['cache_callback_set']) ){ + call_user_func_array( + Less_Parser::$options['cache_callback_set'], + array($this, $file_path, $cache_file, $rules) + ); + } + + }else{ + //msg('write cache file'); + switch(Less_Parser::$options['cache_method']){ + case 'serialize': + file_put_contents( $cache_file, serialize($rules) ); + break; + case 'php': + file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' ); + break; + case 'var_export': + //Requires __set_state() + file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' ); + break; + } + + Less_Cache::CleanCache(); + } + } + + return $rules; + } + + + /** + * Set up the input buffer + * + */ + public function SetInput( $file_path ){ + + if( $file_path ){ + $this->input = file_get_contents( $file_path ); + } + + $this->pos = $this->furthest = 0; + + // Remove potential UTF Byte Order Mark + $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input); + $this->input_len = strlen($this->input); + + + if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){ + $uri = $this->env->currentFileInfo['currentUri']; + Less_Parser::$contentsMap[$uri] = $this->input; + } + + } + + + /** + * Free up some memory + * + */ + public function UnsetInput(){ + unset($this->input, $this->pos, $this->input_len, $this->furthest); + $this->saveStack = array(); + } + + + public function CacheFile( $file_path ){ + + if( $file_path && $this->CacheEnabled() ){ + + $env = get_object_vars($this->env); + unset($env['frames']); + + $parts = array(); + $parts[] = $file_path; + $parts[] = filesize( $file_path ); + $parts[] = filemtime( $file_path ); + $parts[] = $env; + $parts[] = Less_Version::cache_version; + $parts[] = Less_Parser::$options['cache_method']; + return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache'; + } + } + + + static function AddParsedFile($file){ + self::$imports[] = $file; + } + + static function AllParsedFiles(){ + return self::$imports; + } + + /** + * @param string $file + */ + static function FileParsed($file){ + return in_array($file,self::$imports); + } + + + function save() { + $this->saveStack[] = $this->pos; + } + + private function restore() { + $this->pos = array_pop($this->saveStack); + } + + private function forget(){ + array_pop($this->saveStack); + } + + + private function isWhitespace($offset = 0) { + return preg_match('/\s/',$this->input[ $this->pos + $offset]); + } + + /** + * Parse from a token, regexp or string, and move forward if match + * + * @param array $toks + * @return array + */ + private function match($toks){ + + // The match is confirmed, add the match length to `this::pos`, + // and consume any extra white-space characters (' ' || '\n') + // which come after that. The reason for this is that LeSS's + // grammar is mostly white-space insensitive. + // + + foreach($toks as $tok){ + + $char = $tok[0]; + + if( $char === '/' ){ + $match = $this->MatchReg($tok); + + if( $match ){ + return count($match) === 1 ? $match[0] : $match; + } + + }elseif( $char === '#' ){ + $match = $this->MatchChar($tok[1]); + + }else{ + // Non-terminal, match using a function call + $match = $this->$tok(); + + } + + if( $match ){ + return $match; + } + } + } + + /** + * @param string[] $toks + * + * @return string + */ + private function MatchFuncs($toks){ + + if( $this->pos < $this->input_len ){ + foreach($toks as $tok){ + $match = $this->$tok(); + if( $match ){ + return $match; + } + } + } + + } + + // Match a single character in the input, + private function MatchChar($tok){ + if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){ + $this->skipWhitespace(1); + return $tok; + } + } + + // Match a regexp from the current start point + private function MatchReg($tok){ + + if( preg_match($tok, $this->input, $match, 0, $this->pos) ){ + $this->skipWhitespace(strlen($match[0])); + return $match; + } + } + + + /** + * Same as match(), but don't change the state of the parser, + * just return the match. + * + * @param string $tok + * @return integer + */ + public function PeekReg($tok){ + return preg_match($tok, $this->input, $match, 0, $this->pos); + } + + /** + * @param string $tok + */ + public function PeekChar($tok){ + //return ($this->input[$this->pos] === $tok ); + return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok ); + } + + + /** + * @param integer $length + */ + public function skipWhitespace($length){ + + $this->pos += $length; + + for(; $this->pos < $this->input_len; $this->pos++ ){ + $c = $this->input[$this->pos]; + + if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){ + break; + } + } + } + + + /** + * @param string $tok + * @param string|null $msg + */ + public function expect($tok, $msg = NULL) { + $result = $this->match( array($tok) ); + if (!$result) { + $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg ); + } else { + return $result; + } + } + + /** + * @param string $tok + */ + public function expectChar($tok, $msg = null ){ + $result = $this->MatchChar($tok); + if( !$result ){ + $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg ); + }else{ + return $result; + } + } + + // + // Here in, the parsing rules/functions + // + // The basic structure of the syntax tree generated is as follows: + // + // Ruleset -> Rule -> Value -> Expression -> Entity + // + // Here's some LESS code: + // + // .class { + // color: #fff; + // border: 1px solid #000; + // width: @w + 4px; + // > .child {...} + // } + // + // And here's what the parse tree might look like: + // + // Ruleset (Selector '.class', [ + // Rule ("color", Value ([Expression [Color #fff]])) + // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]])) + // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]])) + // Ruleset (Selector [Element '>', '.child'], [...]) + // ]) + // + // In general, most rules will try to parse a token with the `$()` function, and if the return + // value is truly, will return a new node, of the relevant type. Sometimes, we need to check + // first, before parsing, that's when we use `peek()`. + // + + // + // The `primary` rule is the *entry* and *exit* point of the parser. + // The rules here can appear at any level of the parse tree. + // + // The recursive nature of the grammar is an interplay between the `block` + // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, + // as represented by this simplified grammar: + // + // primary → (ruleset | rule)+ + // ruleset → selector+ block + // block → '{' primary '}' + // + // Only at one point is the primary rule not called from the + // block rule: at the root level. + // + private function parsePrimary(){ + $root = array(); + + while( true ){ + + if( $this->pos >= $this->input_len ){ + break; + } + + $node = $this->parseExtend(true); + if( $node ){ + $root = array_merge($root,$node); + continue; + } + + //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective')); + $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective')); + + if( $node ){ + $root[] = $node; + }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){ + break; + } + + if( $this->PeekChar('}') ){ + break; + } + } + + return $root; + } + + + + // We create a Comment node for CSS comments `/* */`, + // but keep the LeSS comments `//` silent, by just skipping + // over them. + private function parseComment(){ + + if( $this->input[$this->pos] !== '/' ){ + return; + } + + if( $this->input[$this->pos+1] === '/' ){ + $match = $this->MatchReg('/\\G\/\/.*/'); + return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo)); + } + + //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/'); + $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors + if( $comment ){ + return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo)); + } + } + + private function parseComments(){ + $comments = array(); + + while( $this->pos < $this->input_len ){ + $comment = $this->parseComment(); + if( !$comment ){ + break; + } + + $comments[] = $comment; + } + + return $comments; + } + + + + // + // A string, which supports escaping " and ' + // + // "milky way" 'he\'s the one!' + // + private function parseEntitiesQuoted() { + $j = $this->pos; + $e = false; + $index = $this->pos; + + if( $this->input[$this->pos] === '~' ){ + $j++; + $e = true; // Escaped strings + } + + if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){ + return; + } + + if ($e) { + $this->MatchChar('~'); + } + + // Fix for #124: match escaped newlines + //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/'); + $str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/'); + + if( $str ){ + $result = $str[0][0] == '"' ? $str[1] : $str[2]; + return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) ); + } + return; + } + + + // + // A catch-all word, such as: + // + // black border-collapse + // + private function parseEntitiesKeyword(){ + + //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/'); + $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/'); + if( $k ){ + $k = $k[0]; + $color = $this->fromKeyword($k); + if( $color ){ + return $color; + } + return $this->NewObj1('Less_Tree_Keyword',$k); + } + } + + // duplicate of Less_Tree_Color::FromKeyword + private function FromKeyword( $keyword ){ + $keyword = strtolower($keyword); + + if( Less_Colors::hasOwnProperty($keyword) ){ + // detect named color + return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1)); + } + + if( $keyword === 'transparent' ){ + return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true)); + } + } + + // + // A function call + // + // rgb(255, 0, 255) + // + // We also try to catch IE's `alpha()`, but let the `alpha` parser + // deal with the details. + // + // The arguments are parsed with the `entities.arguments` parser. + // + private function parseEntitiesCall(){ + $index = $this->pos; + + if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){ + return; + } + $name = $name[1]; + $nameLC = strtolower($name); + + if ($nameLC === 'url') { + return null; + } + + $this->pos += strlen($name); + + if( $nameLC === 'alpha' ){ + $alpha_ret = $this->parseAlpha(); + if( $alpha_ret ){ + return $alpha_ret; + } + } + + $this->MatchChar('('); // Parse the '(' and consume whitespace. + + $args = $this->parseEntitiesArguments(); + + if( !$this->MatchChar(')') ){ + return; + } + + if ($name) { + return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) ); + } + } + + /** + * Parse a list of arguments + * + * @return array + */ + private function parseEntitiesArguments(){ + + $args = array(); + while( true ){ + $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') ); + if( !$arg ){ + break; + } + + $args[] = $arg; + if( !$this->MatchChar(',') ){ + break; + } + } + return $args; + } + + private function parseEntitiesLiteral(){ + return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') ); + } + + // Assignments are argument entities for calls. + // They are present in ie filter properties as shown below. + // + // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) + // + private function parseEntitiesAssignment() { + + $key = $this->MatchReg('/\\G\w+(?=\s?=)/'); + if( !$key ){ + return; + } + + if( !$this->MatchChar('=') ){ + return; + } + + $value = $this->parseEntity(); + if( $value ){ + return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value)); + } + } + + // + // Parse url() tokens + // + // We use a specific rule for urls, because they don't really behave like + // standard function calls. The difference is that the argument doesn't have + // to be enclosed within a string, so it can't be parsed as an Expression. + // + private function parseEntitiesUrl(){ + + + if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){ + return; + } + + $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') ); + if( !$value ){ + $value = ''; + } + + + $this->expectChar(')'); + + + if( isset($value->value) || $value instanceof Less_Tree_Variable ){ + return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo)); + } + + return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) ); + } + + + // + // A Variable entity, such as `@fink`, in + // + // width: @fink + 2px + // + // We use a different parser for variable definitions, + // see `parsers.variable`. + // + private function parseEntitiesVariable(){ + $index = $this->pos; + if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) { + return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo)); + } + } + + + // A variable entity useing the protective {} e.g. @{var} + private function parseEntitiesVariableCurly() { + $index = $this->pos; + + if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){ + return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo)); + } + } + + // + // A Hexadecimal color + // + // #4F3C2F + // + // `rgb` and `hsl` colors are parsed through the `entities.call` parser. + // + private function parseEntitiesColor(){ + if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) { + return $this->NewObj1('Less_Tree_Color',$rgb[1]); + } + } + + // + // A Dimension, that is, a number and a unit + // + // 0.5em 95% + // + private function parseEntitiesDimension(){ + + $c = @ord($this->input[$this->pos]); + + //Is the first char of the dimension 0-9, '.', '+' or '-' + if (($c > 57 || $c < 43) || $c === 47 || $c == 44){ + return; + } + + $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/'); + if( $value ){ + + if( isset($value[2]) ){ + return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2])); + } + return $this->NewObj1('Less_Tree_Dimension',$value[1]); + } + } + + + // + // A unicode descriptor, as is used in unicode-range + // + // U+0?? or U+00A1-00A9 + // + function parseUnicodeDescriptor() { + $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/'); + if( $ud ){ + return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]); + } + } + + + // + // JavaScript code to be evaluated + // + // `window.location.href` + // + private function parseEntitiesJavascript(){ + $e = false; + $j = $this->pos; + if( $this->input[$j] === '~' ){ + $j++; + $e = true; + } + if( $this->input[$j] !== '`' ){ + return; + } + if( $e ){ + $this->MatchChar('~'); + } + $str = $this->MatchReg('/\\G`([^`]*)`/'); + if( $str ){ + return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e)); + } + } + + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink: + // + private function parseVariable(){ + if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) { + return $name[1]; + } + } + + + // + // The variable part of a variable definition. Used in the `rule` parser + // + // @fink(); + // + private function parseRulesetCall(){ + + if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){ + return $this->NewObj1('Less_Tree_RulesetCall', $name[1] ); + } + } + + + // + // extend syntax - used to extend selectors + // + function parseExtend($isRule = false){ + + $index = $this->pos; + $extendList = array(); + + + if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; } + + do{ + $option = null; + $elements = array(); + while( true ){ + $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/'); + if( $option ){ break; } + $e = $this->parseElement(); + if( !$e ){ break; } + $elements[] = $e; + } + + if( $option ){ + $option = $option[1]; + } + + $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index )); + + }while( $this->MatchChar(",") ); + + $this->expect('/\\G\)/'); + + if( $isRule ){ + $this->expect('/\\G;/'); + } + + return $extendList; + } + + + // + // A Mixin call, with an optional argument list + // + // #mixins > .square(#fff); + // .rounded(4px, black); + // .button; + // + // The `while` loop is there because mixins can be + // namespaced, but we only support the child and descendant + // selector for now. + // + private function parseMixinCall(){ + + $char = $this->input[$this->pos]; + if( $char !== '.' && $char !== '#' ){ + return; + } + + $index = $this->pos; + $this->save(); // stop us absorbing part of an invalid selector + + $elements = $this->parseMixinCallElements(); + + if( $elements ){ + + if( $this->MatchChar('(') ){ + $returned = $this->parseMixinArgs(true); + $args = $returned['args']; + $this->expectChar(')'); + }else{ + $args = array(); + } + + $important = $this->parseImportant(); + + if( $this->parseEnd() ){ + $this->forget(); + return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important)); + } + } + + $this->restore(); + } + + + private function parseMixinCallElements(){ + $elements = array(); + $c = null; + + while( true ){ + $elemIndex = $this->pos; + $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/'); + if( !$e ){ + break; + } + $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo)); + $c = $this->MatchChar('>'); + } + + return $elements; + } + + + + /** + * @param boolean $isCall + */ + private function parseMixinArgs( $isCall ){ + $expressions = array(); + $argsSemiColon = array(); + $isSemiColonSeperated = null; + $argsComma = array(); + $expressionContainsNamed = null; + $name = null; + $returner = array('args'=>array(), 'variadic'=> false); + + $this->save(); + + while( true ){ + if( $isCall ){ + $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) ); + } else { + $this->parseComments(); + if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){ + $returner['variadic'] = true; + if( $this->MatchChar(";") && !$isSemiColonSeperated ){ + $isSemiColonSeperated = true; + } + + if( $isSemiColonSeperated ){ + $argsSemiColon[] = array('variadic'=>true); + }else{ + $argsComma[] = array('variadic'=>true); + } + break; + } + $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') ); + } + + if( !$arg ){ + break; + } + + + $nameLoop = null; + if( $arg instanceof Less_Tree_Expression ){ + $arg->throwAwayComments(); + } + $value = $arg; + $val = null; + + if( $isCall ){ + // Variable + if( property_exists($arg,'value') && count($arg->value) == 1 ){ + $val = $arg->value[0]; + } + } else { + $val = $arg; + } + + + if( $val instanceof Less_Tree_Variable ){ + + if( $this->MatchChar(':') ){ + if( $expressions ){ + if( $isSemiColonSeperated ){ + $this->Error('Cannot mix ; and , as delimiter types'); + } + $expressionContainsNamed = true; + } + + // we do not support setting a ruleset as a default variable - it doesn't make sense + // However if we do want to add it, there is nothing blocking it, just don't error + // and remove isCall dependency below + $value = null; + if( $isCall ){ + $value = $this->parseDetachedRuleset(); + } + if( !$value ){ + $value = $this->parseExpression(); + } + + if( !$value ){ + if( $isCall ){ + $this->Error('could not understand value for named argument'); + } else { + $this->restore(); + $returner['args'] = array(); + return $returner; + } + } + + $nameLoop = ($name = $val->name); + }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){ + $returner['variadic'] = true; + if( $this->MatchChar(";") && !$isSemiColonSeperated ){ + $isSemiColonSeperated = true; + } + if( $isSemiColonSeperated ){ + $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true); + }else{ + $argsComma[] = array('name'=> $arg->name, 'variadic' => true); + } + break; + }elseif( !$isCall ){ + $name = $nameLoop = $val->name; + $value = null; + } + } + + if( $value ){ + $expressions[] = $value; + } + + $argsComma[] = array('name'=>$nameLoop, 'value'=>$value ); + + if( $this->MatchChar(',') ){ + continue; + } + + if( $this->MatchChar(';') || $isSemiColonSeperated ){ + + if( $expressionContainsNamed ){ + $this->Error('Cannot mix ; and , as delimiter types'); + } + + $isSemiColonSeperated = true; + + if( count($expressions) > 1 ){ + $value = $this->NewObj1('Less_Tree_Value', $expressions); + } + $argsSemiColon[] = array('name'=>$name, 'value'=>$value ); + + $name = null; + $expressions = array(); + $expressionContainsNamed = false; + } + } + + $this->forget(); + $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma); + return $returner; + } + + + + // + // A Mixin definition, with a list of parameters + // + // .rounded (@radius: 2px, @color) { + // ... + // } + // + // Until we have a finer grained state-machine, we have to + // do a look-ahead, to make sure we don't have a mixin call. + // See the `rule` function for more information. + // + // We start by matching `.rounded (`, and then proceed on to + // the argument list, which has optional default values. + // We store the parameters in `params`, with a `value` key, + // if there is a value, such as in the case of `@radius`. + // + // Once we've got our params list, and a closing `)`, we parse + // the `{...}` block. + // + private function parseMixinDefinition(){ + $cond = null; + + $char = $this->input[$this->pos]; + if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){ + return; + } + + $this->save(); + + $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/'); + if( $match ){ + $name = $match[1]; + + $argInfo = $this->parseMixinArgs( false ); + $params = $argInfo['args']; + $variadic = $argInfo['variadic']; + + + // .mixincall("@{a}"); + // looks a bit like a mixin definition.. + // also + // .mixincall(@a: {rule: set;}); + // so we have to be nice and restore + if( !$this->MatchChar(')') ){ + $this->furthest = $this->pos; + $this->restore(); + return; + } + + + $this->parseComments(); + + if ($this->MatchReg('/\\Gwhen/')) { // Guard + $cond = $this->expect('parseConditions', 'Expected conditions'); + } + + $ruleset = $this->parseBlock(); + + if( is_array($ruleset) ){ + $this->forget(); + return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic)); + } + + $this->restore(); + }else{ + $this->forget(); + } + } + + // + // Entities are the smallest recognized token, + // and can be found inside a rule's value. + // + private function parseEntity(){ + + return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') ); + } + + // + // A Rule terminator. Note that we use `peek()` to check for '}', + // because the `block` rule will be expecting it, but we still need to make sure + // it's there, if ';' was ommitted. + // + private function parseEnd(){ + return $this->MatchChar(';') || $this->PeekChar('}'); + } + + // + // IE's alpha function + // + // alpha(opacity=88) + // + private function parseAlpha(){ + + if ( ! $this->MatchReg('/\\G\(opacity=/i')) { + return; + } + + $value = $this->MatchReg('/\\G[0-9]+/'); + if( $value ){ + $value = $value[0]; + }else{ + $value = $this->parseEntitiesVariable(); + if( !$value ){ + return; + } + } + + $this->expectChar(')'); + return $this->NewObj1('Less_Tree_Alpha',$value); + } + + + // + // A Selector Element + // + // div + // + h1 + // #socks + // input[type="text"] + // + // Elements are the building blocks for Selectors, + // they are made out of a `Combinator` (see combinator rule), + // and an element name, such as a tag a class, or `*`. + // + private function parseElement(){ + $c = $this->parseCombinator(); + $index = $this->pos; + + $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/', + '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') ); + + if( is_null($e) ){ + $this->save(); + if( $this->MatchChar('(') ){ + if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){ + $e = $this->NewObj1('Less_Tree_Paren',$v); + $this->forget(); + }else{ + $this->restore(); + } + }else{ + $this->forget(); + } + } + + if( !is_null($e) ){ + return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo)); + } + } + + // + // Combinators combine elements together, in a Selector. + // + // Because our parser isn't white-space sensitive, special care + // has to be taken, when parsing the descendant combinator, ` `, + // as it's an empty space. We have to check the previous character + // in the input, to see if it's a ` ` character. + // + private function parseCombinator(){ + if( $this->pos < $this->input_len ){ + $c = $this->input[$this->pos]; + if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){ + + $this->pos++; + if( $this->input[$this->pos] === '^' ){ + $c = '^^'; + $this->pos++; + } + + $this->skipWhitespace(0); + + return $c; + } + + if( $this->pos > 0 && $this->isWhitespace(-1) ){ + return ' '; + } + } + } + + // + // A CSS selector (see selector below) + // with less extensions e.g. the ability to extend and guard + // + private function parseLessSelector(){ + return $this->parseSelector(true); + } + + // + // A CSS Selector + // + // .class > div + h1 + // li a:hover + // + // Selectors are made out of one or more Elements, see above. + // + private function parseSelector( $isLess = false ){ + $elements = array(); + $extendList = array(); + $condition = null; + $when = false; + $extend = false; + $e = null; + $c = null; + $index = $this->pos; + + while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){ + if( $when ){ + $condition = $this->expect('parseConditions', 'expected condition'); + }elseif( $condition ){ + //error("CSS guard can only be used at the end of selector"); + }elseif( $extend ){ + $extendList = array_merge($extendList,$extend); + }else{ + //if( count($extendList) ){ + //error("Extend can only be used at the end of selector"); + //} + if( $this->pos < $this->input_len ){ + $c = $this->input[ $this->pos ]; + } + $elements[] = $e; + $e = null; + } + + if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; } + } + + if( $elements ){ + return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo)); + } + if( $extendList ) { + $this->Error('Extend must be used to extend a selector, it cannot be used on its own'); + } + } + + private function parseTag(){ + return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*'); + } + + private function parseAttribute(){ + + $val = null; + + if( !$this->MatchChar('[') ){ + return; + } + + $key = $this->parseEntitiesVariableCurly(); + if( !$key ){ + $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/'); + } + + $op = $this->MatchReg('/\\G[|~*$^]?=/'); + if( $op ){ + $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') ); + } + + $this->expectChar(']'); + + return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val)); + } + + // + // The `block` rule is used by `ruleset` and `mixin.definition`. + // It's a wrapper around the `primary` rule, with added `{}`. + // + private function parseBlock(){ + if( $this->MatchChar('{') ){ + $content = $this->parsePrimary(); + if( $this->MatchChar('}') ){ + return $content; + } + } + } + + private function parseBlockRuleset(){ + $block = $this->parseBlock(); + + if( $block ){ + $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block)); + } + + return $block; + } + + private function parseDetachedRuleset(){ + $blockRuleset = $this->parseBlockRuleset(); + if( $blockRuleset ){ + return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset); + } + } + + // + // div, .class, body > p {...} + // + private function parseRuleset(){ + $selectors = array(); + + $this->save(); + + while( true ){ + $s = $this->parseLessSelector(); + if( !$s ){ + break; + } + $selectors[] = $s; + $this->parseComments(); + + if( $s->condition && count($selectors) > 1 ){ + $this->Error('Guards are only currently allowed on a single selector.'); + } + + if( !$this->MatchChar(',') ){ + break; + } + if( $s->condition ){ + $this->Error('Guards are only currently allowed on a single selector.'); + } + $this->parseComments(); + } + + + if( $selectors ){ + $rules = $this->parseBlock(); + if( is_array($rules) ){ + $this->forget(); + return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports + } + } + + // Backtrack + $this->furthest = $this->pos; + $this->restore(); + } + + /** + * Custom less.php parse function for finding simple name-value css pairs + * ex: width:100px; + * + */ + private function parseNameValue(){ + + $index = $this->pos; + $this->save(); + + + //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/'); + $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/'); + if( $match ){ + + if( $match[4] == '}' ){ + $this->pos = $index + strlen($match[0])-1; + } + + if( $match[3] ){ + $match[2] .= ' !important'; + } + + return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo)); + } + + $this->restore(); + } + + + private function parseRule( $tryAnonymous = null ){ + + $merge = false; + $startOfRule = $this->pos; + + $c = $this->input[$this->pos]; + if( $c === '.' || $c === '#' || $c === '&' ){ + return; + } + + $this->save(); + $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty')); + + if( $name ){ + + $isVariable = is_string($name); + + $value = null; + if( $isVariable ){ + $value = $this->parseDetachedRuleset(); + } + + $important = null; + if( !$value ){ + + // prefer to try to parse first if its a variable or we are compressing + // but always fallback on the other one + //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){ + if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){ + $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue')); + }else{ + $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue')); + } + + $important = $this->parseImportant(); + + // a name returned by this.ruleProperty() is always an array of the form: + // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"] + // where each item is a tree.Keyword or tree.Variable + if( !$isVariable && is_array($name) ){ + $nm = array_pop($name); + if( $nm->value ){ + $merge = $nm->value; + } + } + } + + + if( $value && $this->parseEnd() ){ + $this->forget(); + return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo)); + }else{ + $this->furthest = $this->pos; + $this->restore(); + if( $value && !$tryAnonymous ){ + return $this->parseRule(true); + } + } + }else{ + $this->forget(); + } + } + + function parseAnonymousValue(){ + + if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){ + $this->pos += strlen($match[1]); + return $this->NewObj1('Less_Tree_Anonymous',$match[1]); + } + } + + // + // An @import directive + // + // @import "lib"; + // + // Depending on our environment, importing is done differently: + // In the browser, it's an XHR request, in Node, it would be a + // file-system operation. The function used for importing is + // stored in `import`, which we pass to the Import constructor. + // + private function parseImport(){ + + $this->save(); + + $dir = $this->MatchReg('/\\G@import?\s+/'); + + if( $dir ){ + $options = $this->parseImportOptions(); + $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl')); + + if( $path ){ + $features = $this->parseMediaFeatures(); + if( $this->MatchChar(';') ){ + if( $features ){ + $features = $this->NewObj1('Less_Tree_Value',$features); + } + + $this->forget(); + return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo)); + } + } + } + + $this->restore(); + } + + private function parseImportOptions(){ + + $options = array(); + + // list of options, surrounded by parens + if( !$this->MatchChar('(') ){ + return $options; + } + do{ + $optionName = $this->parseImportOption(); + if( $optionName ){ + $value = true; + switch( $optionName ){ + case "css": + $optionName = "less"; + $value = false; + break; + case "once": + $optionName = "multiple"; + $value = false; + break; + } + $options[$optionName] = $value; + if( !$this->MatchChar(',') ){ break; } + } + }while( $optionName ); + $this->expectChar(')'); + return $options; + } + + private function parseImportOption(){ + $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/'); + if( $opt ){ + return $opt[1]; + } + } + + private function parseMediaFeature() { + $nodes = array(); + + do{ + $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable')); + if( $e ){ + $nodes[] = $e; + } elseif ($this->MatchChar('(')) { + $p = $this->parseProperty(); + $e = $this->parseValue(); + if ($this->MatchChar(')')) { + if ($p && $e) { + $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true)); + $nodes[] = $this->NewObj1('Less_Tree_Paren',$r); + } elseif ($e) { + $nodes[] = $this->NewObj1('Less_Tree_Paren',$e); + } else { + return null; + } + } else + return null; + } + } while ($e); + + if ($nodes) { + return $this->NewObj1('Less_Tree_Expression',$nodes); + } + } + + private function parseMediaFeatures() { + $features = array(); + + do{ + $e = $this->parseMediaFeature(); + if( $e ){ + $features[] = $e; + if (!$this->MatchChar(',')) break; + }else{ + $e = $this->parseEntitiesVariable(); + if( $e ){ + $features[] = $e; + if (!$this->MatchChar(',')) break; + } + } + } while ($e); + + return $features ? $features : null; + } + + private function parseMedia() { + if( $this->MatchReg('/\\G@media/') ){ + $features = $this->parseMediaFeatures(); + $rules = $this->parseBlock(); + + if( is_array($rules) ){ + return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo)); + } + } + } + + + // + // A CSS Directive + // + // @charset "utf-8"; + // + private function parseDirective(){ + + if( !$this->PeekChar('@') ){ + return; + } + + $rules = null; + $index = $this->pos; + $hasBlock = true; + $hasIdentifier = false; + $hasExpression = false; + $hasUnknown = false; + + + $value = $this->MatchFuncs(array('parseImport','parseMedia')); + if( $value ){ + return $value; + } + + $this->save(); + + $name = $this->MatchReg('/\\G@[a-z-]+/'); + + if( !$name ) return; + $name = $name[0]; + + + $nonVendorSpecificName = $name; + $pos = strpos($name,'-', 2); + if( $name[1] == '-' && $pos > 0 ){ + $nonVendorSpecificName = "@" . substr($name, $pos + 1); + } + + + switch( $nonVendorSpecificName ){ + /* + case "@font-face": + case "@viewport": + case "@top-left": + case "@top-left-corner": + case "@top-center": + case "@top-right": + case "@top-right-corner": + case "@bottom-left": + case "@bottom-left-corner": + case "@bottom-center": + case "@bottom-right": + case "@bottom-right-corner": + case "@left-top": + case "@left-middle": + case "@left-bottom": + case "@right-top": + case "@right-middle": + case "@right-bottom": + hasBlock = true; + break; + */ + case "@charset": + $hasIdentifier = true; + $hasBlock = false; + break; + case "@namespace": + $hasExpression = true; + $hasBlock = false; + break; + case "@keyframes": + $hasIdentifier = true; + break; + case "@host": + case "@page": + case "@document": + case "@supports": + $hasUnknown = true; + break; + } + + if( $hasIdentifier ){ + $value = $this->parseEntity(); + if( !$value ){ + $this->error("expected " . $name . " identifier"); + } + } else if( $hasExpression ){ + $value = $this->parseExpression(); + if( !$value ){ + $this->error("expected " . $name. " expression"); + } + } else if ($hasUnknown) { + + $value = $this->MatchReg('/\\G[^{;]+/'); + if( $value ){ + $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0])); + } + } + + if( $hasBlock ){ + $rules = $this->parseBlockRuleset(); + } + + if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) { + $this->forget(); + return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo)); + } + + $this->restore(); + } + + + // + // A Value is a comma-delimited list of Expressions + // + // font-family: Baskerville, Georgia, serif; + // + // In a Rule, a Value represents everything after the `:`, + // and before the `;`. + // + private function parseValue(){ + $expressions = array(); + + do{ + $e = $this->parseExpression(); + if( $e ){ + $expressions[] = $e; + if (! $this->MatchChar(',')) { + break; + } + } + }while($e); + + if( $expressions ){ + return $this->NewObj1('Less_Tree_Value',$expressions); + } + } + + private function parseImportant (){ + if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){ + return ' !important'; + } + } + + private function parseSub (){ + + if( $this->MatchChar('(') ){ + $a = $this->parseAddition(); + if( $a ){ + $this->expectChar(')'); + return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached + } + } + } + + + /** + * Parses multiplication operation + * + * @return Less_Tree_Operation|null + */ + function parseMultiplication(){ + + $return = $m = $this->parseOperand(); + if( $return ){ + while( true ){ + + $isSpaced = $this->isWhitespace( -1 ); + + if( $this->PeekReg('/\\G\/[*\/]/') ){ + break; + } + + $op = $this->MatchChar('/'); + if( !$op ){ + $op = $this->MatchChar('*'); + if( !$op ){ + break; + } + } + + $a = $this->parseOperand(); + + if(!$a) { break; } + + $m->parensInOp = true; + $a->parensInOp = true; + $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) ); + } + } + return $return; + + } + + + /** + * Parses an addition operation + * + * @return Less_Tree_Operation|null + */ + private function parseAddition (){ + + $return = $m = $this->parseMultiplication(); + if( $return ){ + while( true ){ + + $isSpaced = $this->isWhitespace( -1 ); + + $op = $this->MatchReg('/\\G[-+]\s+/'); + if( $op ){ + $op = $op[0]; + }else{ + if( !$isSpaced ){ + $op = $this->match(array('#+','#-')); + } + if( !$op ){ + break; + } + } + + $a = $this->parseMultiplication(); + if( !$a ){ + break; + } + + $m->parensInOp = true; + $a->parensInOp = true; + $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced)); + } + } + + return $return; + } + + + /** + * Parses the conditions + * + * @return Less_Tree_Condition|null + */ + private function parseConditions() { + $index = $this->pos; + $return = $a = $this->parseCondition(); + if( $a ){ + while( true ){ + if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){ + break; + } + $b = $this->parseCondition(); + if( !$b ){ + break; + } + + $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index)); + } + return $return; + } + } + + private function parseCondition() { + $index = $this->pos; + $negate = false; + $c = null; + + if ($this->MatchReg('/\\Gnot/')) $negate = true; + $this->expectChar('('); + $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); + + if( $a ){ + $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/'); + if( $op ){ + $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); + if( $b ){ + $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate)); + } else { + $this->Error('Unexpected expression'); + } + } else { + $k = $this->NewObj1('Less_Tree_Keyword','true'); + $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate)); + } + $this->expectChar(')'); + return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c; + } + } + + /** + * An operand is anything that can be part of an operation, + * such as a Color, or a Variable + * + */ + private function parseOperand (){ + + $negate = false; + $offset = $this->pos+1; + if( $offset >= $this->input_len ){ + return; + } + $char = $this->input[$offset]; + if( $char === '@' || $char === '(' ){ + $negate = $this->MatchChar('-'); + } + + $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall')); + + if( $negate ){ + $o->parensInOp = true; + $o = $this->NewObj1('Less_Tree_Negative',$o); + } + + return $o; + } + + + /** + * Expressions either represent mathematical operations, + * or white-space delimited Entities. + * + * 1px solid black + * @var * 2 + * + * @return Less_Tree_Expression|null + */ + private function parseExpression (){ + $entities = array(); + + do{ + $e = $this->MatchFuncs(array('parseAddition','parseEntity')); + if( $e ){ + $entities[] = $e; + // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here + if( !$this->PeekReg('/\\G\/[\/*]/') ){ + $delim = $this->MatchChar('/'); + if( $delim ){ + $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim); + } + } + } + }while($e); + + if( $entities ){ + return $this->NewObj1('Less_Tree_Expression',$entities); + } + } + + + /** + * Parse a property + * eg: 'min-width', 'orientation', etc + * + * @return string + */ + private function parseProperty (){ + $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/'); + if( $name ){ + return $name[1]; + } + } + + + /** + * Parse a rule property + * eg: 'color', 'width', 'height', etc + * + * @return string + */ + private function parseRuleProperty(){ + $offset = $this->pos; + $name = array(); + $index = array(); + $length = 0; + + + $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name ); + while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // ! + + if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){ + // at last, we have the complete match now. move forward, + // convert name particles to tree objects and return: + $this->skipWhitespace($length); + + if( $name[0] === '' ){ + array_shift($name); + array_shift($index); + } + foreach($name as $k => $s ){ + if( !$s || $s[0] !== '@' ){ + $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s); + }else{ + $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo)); + } + } + return $name; + } + + + } + + private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){ + preg_match($re, $this->input, $a, 0, $offset); + if( $a ){ + $index[] = $this->pos + $length; + $length += strlen($a[0]); + $offset += strlen($a[0]); + $name[] = $a[1]; + return true; + } + } + + public static function serializeVars( $vars ){ + $s = ''; + + foreach($vars as $name => $value){ + $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';'); + } + + return $s; + } + + + /** + * Some versions of php have trouble with method_exists($a,$b) if $a is not an object + * + * @param string $b + */ + public static function is_method($a,$b){ + return is_object($a) && method_exists($a,$b); + } + + + /** + * Round numbers similarly to javascript + * eg: 1.499999 to 1 instead of 2 + * + */ + public static function round($i, $precision = 0){ + + $precision = pow(10,$precision); + $i = $i*$precision; + + $ceil = ceil($i); + $floor = floor($i); + if( ($ceil - $i) <= ($i - $floor) ){ + return $ceil/$precision; + }else{ + return $floor/$precision; + } + } + + + /** + * Create Less_Tree_* objects and optionally generate a cache string + * + * @return mixed + */ + public function NewObj0($class){ + $obj = new $class(); + if( $this->CacheEnabled() ){ + $obj->cache_string = ' new '.$class.'()'; + } + return $obj; + } + + public function NewObj1($class, $arg){ + $obj = new $class( $arg ); + if( $this->CacheEnabled() ){ + $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')'; + } + return $obj; + } + + public function NewObj2($class, $args){ + $obj = new $class( $args[0], $args[1] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + public function NewObj3($class, $args){ + $obj = new $class( $args[0], $args[1], $args[2] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + public function NewObj4($class, $args){ + $obj = new $class( $args[0], $args[1], $args[2], $args[3] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + public function NewObj5($class, $args){ + $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + public function NewObj6($class, $args){ + $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + public function NewObj7($class, $args){ + $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] ); + if( $this->CacheEnabled() ){ + $this->ObjCache( $obj, $class, $args); + } + return $obj; + } + + //caching + public function ObjCache($obj, $class, $args=array()){ + $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')'; + } + + public function ArgCache($args){ + return implode(',',array_map( array('Less_Parser','ArgString'),$args)); + } + + + /** + * Convert an argument to a string for use in the parser cache + * + * @return string + */ + public static function ArgString($arg){ + + $type = gettype($arg); + + if( $type === 'object'){ + $string = $arg->cache_string; + unset($arg->cache_string); + return $string; + + }elseif( $type === 'array' ){ + $string = ' Array('; + foreach($arg as $k => $a){ + $string .= var_export($k,true).' => '.self::ArgString($a).','; + } + return $string . ')'; + } + + return var_export($arg,true); + } + + public function Error($msg){ + throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo); + } + + public static function WinPath($path){ + return str_replace('\\', '/', $path); + } + + public function CacheEnabled(){ + return false; + //return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback'))); + } + +} + + +/** + * Utility for css colors + * + * @package Less + * @subpackage color + */ +class Less_Colors { + + public static $colors = array( + 'aliceblue'=>'#f0f8ff', + 'antiquewhite'=>'#faebd7', + 'aqua'=>'#00ffff', + 'aquamarine'=>'#7fffd4', + 'azure'=>'#f0ffff', + 'beige'=>'#f5f5dc', + 'bisque'=>'#ffe4c4', + 'black'=>'#000000', + 'blanchedalmond'=>'#ffebcd', + 'blue'=>'#0000ff', + 'blueviolet'=>'#8a2be2', + 'brown'=>'#a52a2a', + 'burlywood'=>'#deb887', + 'cadetblue'=>'#5f9ea0', + 'chartreuse'=>'#7fff00', + 'chocolate'=>'#d2691e', + 'coral'=>'#ff7f50', + 'cornflowerblue'=>'#6495ed', + 'cornsilk'=>'#fff8dc', + 'crimson'=>'#dc143c', + 'cyan'=>'#00ffff', + 'darkblue'=>'#00008b', + 'darkcyan'=>'#008b8b', + 'darkgoldenrod'=>'#b8860b', + 'darkgray'=>'#a9a9a9', + 'darkgrey'=>'#a9a9a9', + 'darkgreen'=>'#006400', + 'darkkhaki'=>'#bdb76b', + 'darkmagenta'=>'#8b008b', + 'darkolivegreen'=>'#556b2f', + 'darkorange'=>'#ff8c00', + 'darkorchid'=>'#9932cc', + 'darkred'=>'#8b0000', + 'darksalmon'=>'#e9967a', + 'darkseagreen'=>'#8fbc8f', + 'darkslateblue'=>'#483d8b', + 'darkslategray'=>'#2f4f4f', + 'darkslategrey'=>'#2f4f4f', + 'darkturquoise'=>'#00ced1', + 'darkviolet'=>'#9400d3', + 'deeppink'=>'#ff1493', + 'deepskyblue'=>'#00bfff', + 'dimgray'=>'#696969', + 'dimgrey'=>'#696969', + 'dodgerblue'=>'#1e90ff', + 'firebrick'=>'#b22222', + 'floralwhite'=>'#fffaf0', + 'forestgreen'=>'#228b22', + 'fuchsia'=>'#ff00ff', + 'gainsboro'=>'#dcdcdc', + 'ghostwhite'=>'#f8f8ff', + 'gold'=>'#ffd700', + 'goldenrod'=>'#daa520', + 'gray'=>'#808080', + 'grey'=>'#808080', + 'green'=>'#008000', + 'greenyellow'=>'#adff2f', + 'honeydew'=>'#f0fff0', + 'hotpink'=>'#ff69b4', + 'indianred'=>'#cd5c5c', + 'indigo'=>'#4b0082', + 'ivory'=>'#fffff0', + 'khaki'=>'#f0e68c', + 'lavender'=>'#e6e6fa', + 'lavenderblush'=>'#fff0f5', + 'lawngreen'=>'#7cfc00', + 'lemonchiffon'=>'#fffacd', + 'lightblue'=>'#add8e6', + 'lightcoral'=>'#f08080', + 'lightcyan'=>'#e0ffff', + 'lightgoldenrodyellow'=>'#fafad2', + 'lightgray'=>'#d3d3d3', + 'lightgrey'=>'#d3d3d3', + 'lightgreen'=>'#90ee90', + 'lightpink'=>'#ffb6c1', + 'lightsalmon'=>'#ffa07a', + 'lightseagreen'=>'#20b2aa', + 'lightskyblue'=>'#87cefa', + 'lightslategray'=>'#778899', + 'lightslategrey'=>'#778899', + 'lightsteelblue'=>'#b0c4de', + 'lightyellow'=>'#ffffe0', + 'lime'=>'#00ff00', + 'limegreen'=>'#32cd32', + 'linen'=>'#faf0e6', + 'magenta'=>'#ff00ff', + 'maroon'=>'#800000', + 'mediumaquamarine'=>'#66cdaa', + 'mediumblue'=>'#0000cd', + 'mediumorchid'=>'#ba55d3', + 'mediumpurple'=>'#9370d8', + 'mediumseagreen'=>'#3cb371', + 'mediumslateblue'=>'#7b68ee', + 'mediumspringgreen'=>'#00fa9a', + 'mediumturquoise'=>'#48d1cc', + 'mediumvioletred'=>'#c71585', + 'midnightblue'=>'#191970', + 'mintcream'=>'#f5fffa', + 'mistyrose'=>'#ffe4e1', + 'moccasin'=>'#ffe4b5', + 'navajowhite'=>'#ffdead', + 'navy'=>'#000080', + 'oldlace'=>'#fdf5e6', + 'olive'=>'#808000', + 'olivedrab'=>'#6b8e23', + 'orange'=>'#ffa500', + 'orangered'=>'#ff4500', + 'orchid'=>'#da70d6', + 'palegoldenrod'=>'#eee8aa', + 'palegreen'=>'#98fb98', + 'paleturquoise'=>'#afeeee', + 'palevioletred'=>'#d87093', + 'papayawhip'=>'#ffefd5', + 'peachpuff'=>'#ffdab9', + 'peru'=>'#cd853f', + 'pink'=>'#ffc0cb', + 'plum'=>'#dda0dd', + 'powderblue'=>'#b0e0e6', + 'purple'=>'#800080', + 'red'=>'#ff0000', + 'rosybrown'=>'#bc8f8f', + 'royalblue'=>'#4169e1', + 'saddlebrown'=>'#8b4513', + 'salmon'=>'#fa8072', + 'sandybrown'=>'#f4a460', + 'seagreen'=>'#2e8b57', + 'seashell'=>'#fff5ee', + 'sienna'=>'#a0522d', + 'silver'=>'#c0c0c0', + 'skyblue'=>'#87ceeb', + 'slateblue'=>'#6a5acd', + 'slategray'=>'#708090', + 'slategrey'=>'#708090', + 'snow'=>'#fffafa', + 'springgreen'=>'#00ff7f', + 'steelblue'=>'#4682b4', + 'tan'=>'#d2b48c', + 'teal'=>'#008080', + 'thistle'=>'#d8bfd8', + 'tomato'=>'#ff6347', + 'turquoise'=>'#40e0d0', + 'violet'=>'#ee82ee', + 'wheat'=>'#f5deb3', + 'white'=>'#ffffff', + 'whitesmoke'=>'#f5f5f5', + 'yellow'=>'#ffff00', + 'yellowgreen'=>'#9acd32' + ); + + public static function hasOwnProperty($color) { + return isset(self::$colors[$color]); + } + + + public static function color($color) { + return self::$colors[$color]; + } + +} + + + +/** + * Environment + * + * @package Less + * @subpackage environment + */ +class Less_Environment{ + + //public $paths = array(); // option - unmodified - paths to search for imports on + //public static $files = array(); // list of files that have been imported, used for import-once + //public $rootpath; // option - rootpath to append to URL's + //public static $strictImports = null; // option - + //public $insecure; // option - whether to allow imports from insecure ssl hosts + //public $processImports; // option - whether to process imports. if false then imports will not be imported + //public $javascriptEnabled; // option - whether JavaScript is enabled. if undefined, defaults to true + //public $useFileCache; // browser only - whether to use the per file session cache + public $currentFileInfo; // information about the current file - for error reporting and importing and making urls relative etc. + + public $importMultiple = false; // whether we are currently importing multiple copies + + + /** + * @var array + */ + public $frames = array(); + + /** + * @var array + */ + public $mediaBlocks = array(); + + /** + * @var array + */ + public $mediaPath = array(); + + public static $parensStack = 0; + + public static $tabLevel = 0; + + public static $lastRule = false; + + public static $_outputMap; + + public static $mixin_stack = 0; + + /** + * @var array + */ + public $functions = array(); + + + public function Init(){ + + self::$parensStack = 0; + self::$tabLevel = 0; + self::$lastRule = false; + self::$mixin_stack = 0; + + if( Less_Parser::$options['compress'] ){ + + Less_Environment::$_outputMap = array( + ',' => ',', + ': ' => ':', + '' => '', + ' ' => ' ', + ':' => ' :', + '+' => '+', + '~' => '~', + '>' => '>', + '|' => '|', + '^' => '^', + '^^' => '^^' + ); + + }else{ + + Less_Environment::$_outputMap = array( + ',' => ', ', + ': ' => ': ', + '' => '', + ' ' => ' ', + ':' => ' :', + '+' => ' + ', + '~' => ' ~ ', + '>' => ' > ', + '|' => '|', + '^' => ' ^ ', + '^^' => ' ^^ ' + ); + + } + } + + + public function copyEvalEnv($frames = array() ){ + $new_env = new Less_Environment(); + $new_env->frames = $frames; + return $new_env; + } + + + public static function isMathOn(){ + return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack; + } + + public static function isPathRelative($path){ + return !preg_match('/^(?:[a-z-]+:|\/)/',$path); + } + + + /** + * Canonicalize a path by resolving references to '/./', '/../' + * Does not remove leading "../" + * @param string path or url + * @return string Canonicalized path + * + */ + public static function normalizePath($path){ + + $segments = explode('/',$path); + $segments = array_reverse($segments); + + $path = array(); + $path_len = 0; + + while( $segments ){ + $segment = array_pop($segments); + switch( $segment ) { + + case '.': + break; + + case '..': + if( !$path_len || ( $path[$path_len-1] === '..') ){ + $path[] = $segment; + $path_len++; + }else{ + array_pop($path); + $path_len--; + } + break; + + default: + $path[] = $segment; + $path_len++; + break; + } + } + + return implode('/',$path); + } + + + public function unshiftFrame($frame){ + array_unshift($this->frames, $frame); + } + + public function shiftFrame(){ + return array_shift($this->frames); + } + +} + + +/** + * Builtin functions + * + * @package Less + * @subpackage function + * @see http://lesscss.org/functions/ + */ +class Less_Functions{ + + public $env; + public $currentFileInfo; + + function __construct($env, $currentFileInfo = null ){ + $this->env = $env; + $this->currentFileInfo = $currentFileInfo; + } + + /** + * @param string $op + */ + public static function operate( $op, $a, $b ){ + switch ($op) { + case '+': return $a + $b; + case '-': return $a - $b; + case '*': return $a * $b; + case '/': return $a / $b; + } + } + + public static function clamp($val, $max = 1){ + return min( max($val, 0), $max); + } + + public static function fround( $value ){ + + if( $value === 0 ){ + return $value; + } + + if( Less_Parser::$options['numPrecision'] ){ + $p = pow(10, Less_Parser::$options['numPrecision']); + return round( $value * $p) / $p; + } + return $value; + } + + public static function number($n){ + + if ($n instanceof Less_Tree_Dimension) { + return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value); + } else if (is_numeric($n)) { + return $n; + } else { + throw new Less_Exception_Compiler("color functions take numbers as parameters"); + } + } + + public static function scaled($n, $size = 255 ){ + if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){ + return (float)$n->value * $size / 100; + } else { + return Less_Functions::number($n); + } + } + + public function rgb ($r = null, $g = null, $b = null){ + if (is_null($r) || is_null($g) || is_null($b)) { + throw new Less_Exception_Compiler("rgb expects three parameters"); + } + return $this->rgba($r, $g, $b, 1.0); + } + + public function rgba($r = null, $g = null, $b = null, $a = null){ + $rgb = array($r, $g, $b); + $rgb = array_map(array('Less_Functions','scaled'),$rgb); + + $a = self::number($a); + return new Less_Tree_Color($rgb, $a); + } + + public function hsl($h, $s, $l){ + return $this->hsla($h, $s, $l, 1.0); + } + + public function hsla($h, $s, $l, $a){ + + $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int + $s = self::clamp(self::number($s)); + $l = self::clamp(self::number($l)); + $a = self::clamp(self::number($a)); + + $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s; + + $m1 = $l * 2 - $m2; + + return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255, + self::hsla_hue($h, $m1, $m2) * 255, + self::hsla_hue($h - 1/3, $m1, $m2) * 255, + $a); + } + + /** + * @param double $h + */ + public function hsla_hue($h, $m1, $m2){ + $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h); + if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6; + else if ($h * 2 < 1) return $m2; + else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6; + else return $m1; + } + + public function hsv($h, $s, $v) { + return $this->hsva($h, $s, $v, 1.0); + } + + /** + * @param double $a + */ + public function hsva($h, $s, $v, $a) { + $h = ((Less_Functions::number($h) % 360) / 360 ) * 360; + $s = Less_Functions::number($s); + $v = Less_Functions::number($v); + $a = Less_Functions::number($a); + + $i = floor(($h / 60) % 6); + $f = ($h / 60) - $i; + + $vs = array( $v, + $v * (1 - $s), + $v * (1 - $f * $s), + $v * (1 - (1 - $f) * $s)); + + $perm = array(array(0, 3, 1), + array(2, 0, 1), + array(1, 0, 3), + array(1, 2, 0), + array(3, 1, 0), + array(0, 1, 2)); + + return $this->rgba($vs[$perm[$i][0]] * 255, + $vs[$perm[$i][1]] * 255, + $vs[$perm[$i][2]] * 255, + $a); + } + + public function hue($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['h'])); + } + + public function saturation($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%'); + } + + public function lightness($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%'); + } + + public function hsvhue( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) ); + } + + + public function hsvsaturation( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' ); + } + + public function hsvvalue( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsv = $color->toHSV(); + return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' ); + } + + public function red($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[0] ); + } + + public function green($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[1] ); + } + + public function blue($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension( $color->rgb[2] ); + } + + public function alpha($color = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $c = $color->toHSL(); + return new Less_Tree_Dimension($c['a']); + } + + public function luma ($color = null) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%'); + } + + public function luminance( $color = null ){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $luminance = + (0.2126 * $color->rgb[0] / 255) + + (0.7152 * $color->rgb[1] / 255) + + (0.0722 * $color->rgb[2] / 255); + + return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%'); + } + + public function saturate($color = null, $amount = null){ + // filter: saturate(3.2); + // should be kept as is, so check for color + if ($color instanceof Less_Tree_Dimension) { + return null; + } + + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['s'] += $amount->value / 100; + $hsl['s'] = self::clamp($hsl['s']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + /** + * @param Less_Tree_Dimension $amount + */ + public function desaturate($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['s'] -= $amount->value / 100; + $hsl['s'] = self::clamp($hsl['s']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + + + public function lighten($color = null, $amount=null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['l'] += $amount->value / 100; + $hsl['l'] = self::clamp($hsl['l']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function darken($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['l'] -= $amount->value / 100; + $hsl['l'] = self::clamp($hsl['l']); + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fadein($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['a'] += $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fadeout($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hsl['a'] -= $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + public function fade($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + + $hsl['a'] = $amount->value / 100; + $hsl['a'] = self::clamp($hsl['a']); + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + + + public function spin($color = null, $amount = null){ + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$amount instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $hsl = $color->toHSL(); + $hue = fmod($hsl['h'] + $amount->value, 360); + + $hsl['h'] = $hue < 0 ? 360 + $hue : $hue; + + return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); + } + + // + // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein + // http://sass-lang.com + // + + /** + * @param Less_Tree_Color $color1 + */ + public function mix($color1 = null, $color2 = null, $weight = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$weight) { + $weight = new Less_Tree_Dimension('50', '%'); + } + if (!$weight instanceof Less_Tree_Dimension) { + throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + $p = $weight->value / 100.0; + $w = $p * 2 - 1; + $hsl1 = $color1->toHSL(); + $hsl2 = $color2->toHSL(); + $a = $hsl1['a'] - $hsl2['a']; + + $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2; + $w2 = 1 - $w1; + + $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2, + $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2, + $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2); + + $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p); + + return new Less_Tree_Color($rgb, $alpha); + } + + public function greyscale($color){ + return $this->desaturate($color, new Less_Tree_Dimension(100,'%')); + } + + + public function contrast( $color, $dark = null, $light = null, $threshold = null){ + // filter: contrast(3.2); + // should be kept as is, so check for color + if (!$color instanceof Less_Tree_Color) { + return null; + } + if( !$light ){ + $light = $this->rgba(255, 255, 255, 1.0); + } + if( !$dark ){ + $dark = $this->rgba(0, 0, 0, 1.0); + } + + if (!$dark instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$light instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + //Figure out which is actually light and dark! + if( $dark->luma() > $light->luma() ){ + $t = $light; + $light = $dark; + $dark = $t; + } + if( !$threshold ){ + $threshold = 0.43; + } else { + $threshold = Less_Functions::number($threshold); + } + + if( $color->luma() < $threshold ){ + return $light; + } else { + return $dark; + } + } + + public function e ($str){ + if( is_string($str) ){ + return new Less_Tree_Anonymous($str); + } + return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value); + } + + public function escape ($str){ + + $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$'); + + return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert)); + } + + + /** + * todo: This function will need some additional work to make it work the same as less.js + * + */ + public function replace( $string, $pattern, $replacement, $flags = null ){ + $result = $string->value; + + $expr = '/'.str_replace('/','\\/',$pattern->value).'/'; + if( $flags && $flags->value){ + $expr .= self::replace_flags($flags->value); + } + + $result = preg_replace($expr,$replacement->value,$result); + + + if( property_exists($string,'quote') ){ + return new Less_Tree_Quoted( $string->quote, $result, $string->escaped); + } + return new Less_Tree_Quoted( '', $result ); + } + + public static function replace_flags($flags){ + $flags = str_split($flags,1); + $new_flags = ''; + + foreach($flags as $flag){ + switch($flag){ + case 'e': + case 'g': + break; + + default: + $new_flags .= $flag; + break; + } + } + + return $new_flags; + } + + public function _percent(){ + $string = func_get_arg(0); + + $args = func_get_args(); + array_shift($args); + $result = $string->value; + + foreach($args as $arg){ + if( preg_match('/%[sda]/i',$result, $token) ){ + $token = $token[0]; + $value = stristr($token, 's') ? $arg->value : $arg->toCSS(); + $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value; + $result = preg_replace('/%[sda]/i',$value, $result, 1); + } + } + $result = str_replace('%%', '%', $result); + + return new Less_Tree_Quoted( $string->quote , $result, $string->escaped); + } + + public function unit( $val, $unit = null) { + if( !($val instanceof Less_Tree_Dimension) ){ + throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') ); + } + + if( $unit ){ + if( $unit instanceof Less_Tree_Keyword ){ + $unit = $unit->value; + } else { + $unit = $unit->toCSS(); + } + } else { + $unit = ""; + } + return new Less_Tree_Dimension($val->value, $unit ); + } + + public function convert($val, $unit){ + return $val->convertTo($unit->value); + } + + public function round($n, $f = false) { + + $fraction = 0; + if( $f !== false ){ + $fraction = $f->value; + } + + return $this->_math('Less_Parser::round',null, $n, $fraction); + } + + public function pi(){ + return new Less_Tree_Dimension(M_PI); + } + + public function mod($a, $b) { + return new Less_Tree_Dimension( $a->value % $b->value, $a->unit); + } + + + + public function pow($x, $y) { + if( is_numeric($x) && is_numeric($y) ){ + $x = new Less_Tree_Dimension($x); + $y = new Less_Tree_Dimension($y); + }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){ + throw new Less_Exception_Compiler('Arguments must be numbers'); + } + + return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit ); + } + + // var mathFunctions = [{name:"ce ... + public function ceil( $n ){ return $this->_math('ceil', null, $n); } + public function floor( $n ){ return $this->_math('floor', null, $n); } + public function sqrt( $n ){ return $this->_math('sqrt', null, $n); } + public function abs( $n ){ return $this->_math('abs', null, $n); } + + public function tan( $n ){ return $this->_math('tan', '', $n); } + public function sin( $n ){ return $this->_math('sin', '', $n); } + public function cos( $n ){ return $this->_math('cos', '', $n); } + + public function atan( $n ){ return $this->_math('atan', 'rad', $n); } + public function asin( $n ){ return $this->_math('asin', 'rad', $n); } + public function acos( $n ){ return $this->_math('acos', 'rad', $n); } + + private function _math() { + $args = func_get_args(); + $fn = array_shift($args); + $unit = array_shift($args); + + if ($args[0] instanceof Less_Tree_Dimension) { + + if( $unit === null ){ + $unit = $args[0]->unit; + }else{ + $args[0] = $args[0]->unify(); + } + $args[0] = (float)$args[0]->value; + return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit); + } else if (is_numeric($args[0])) { + return call_user_func_array($fn,$args); + } else { + throw new Less_Exception_Compiler("math functions take numbers as parameters"); + } + } + + /** + * @param boolean $isMin + */ + private function _minmax( $isMin, $args ){ + + $arg_count = count($args); + + if( $arg_count < 1 ){ + throw new Less_Exception_Compiler( 'one or more arguments required'); + } + + $j = null; + $unitClone = null; + $unitStatic = null; + + + $order = array(); // elems only contains original argument values. + $values = array(); // key is the unit.toString() for unified tree.Dimension values, + // value is the index into the order array. + + + for( $i = 0; $i < $arg_count; $i++ ){ + $current = $args[$i]; + if( !($current instanceof Less_Tree_Dimension) ){ + if( is_array($args[$i]->value) ){ + $args[] = $args[$i]->value; + } + continue; + } + + if( $current->unit->toString() === '' && !$unitClone ){ + $temp = new Less_Tree_Dimension($current->value, $unitClone); + $currentUnified = $temp->unify(); + }else{ + $currentUnified = $current->unify(); + } + + if( $currentUnified->unit->toString() === "" && !$unitStatic ){ + $unit = $unitStatic; + }else{ + $unit = $currentUnified->unit->toString(); + } + + if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){ + $unitStatic = $unit; + } + + if( $unit != '' && !$unitClone ){ + $unitClone = $current->unit->toString(); + } + + if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){ + $j = $values['']; + }elseif( isset($values[$unit]) ){ + $j = $values[$unit]; + }else{ + + if( $unitStatic && $unit !== $unitStatic ){ + throw new Less_Exception_Compiler( 'incompatible types'); + } + $values[$unit] = count($order); + $order[] = $current; + continue; + } + + + if( $order[$j]->unit->toString() === "" && $unitClone ){ + $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone); + $referenceUnified = $temp->unify(); + }else{ + $referenceUnified = $order[$j]->unify(); + } + if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){ + $order[$j] = $current; + } + } + + if( count($order) == 1 ){ + return $order[0]; + } + $args = array(); + foreach($order as $a){ + $args[] = $a->toCSS($this->env); + } + return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')'); + } + + public function min(){ + $args = func_get_args(); + return $this->_minmax( true, $args ); + } + + public function max(){ + $args = func_get_args(); + return $this->_minmax( false, $args ); + } + + public function getunit($n){ + return new Less_Tree_Anonymous($n->unit); + } + + public function argb($color) { + if (!$color instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return new Less_Tree_Anonymous($color->toARGB()); + } + + public function percentage($n) { + return new Less_Tree_Dimension($n->value * 100, '%'); + } + + public function color($n) { + + if( $n instanceof Less_Tree_Quoted ){ + $colorCandidate = $n->value; + $returnColor = Less_Tree_Color::fromKeyword($colorCandidate); + if( $returnColor ){ + return $returnColor; + } + if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){ + return new Less_Tree_Color(substr($colorCandidate, 1)); + } + throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF"); + } else { + throw new Less_Exception_Compiler("argument must be a string"); + } + } + + + public function iscolor($n) { + return $this->_isa($n, 'Less_Tree_Color'); + } + + public function isnumber($n) { + return $this->_isa($n, 'Less_Tree_Dimension'); + } + + public function isstring($n) { + return $this->_isa($n, 'Less_Tree_Quoted'); + } + + public function iskeyword($n) { + return $this->_isa($n, 'Less_Tree_Keyword'); + } + + public function isurl($n) { + return $this->_isa($n, 'Less_Tree_Url'); + } + + public function ispixel($n) { + return $this->isunit($n, 'px'); + } + + public function ispercentage($n) { + return $this->isunit($n, '%'); + } + + public function isem($n) { + return $this->isunit($n, 'em'); + } + + /** + * @param string $unit + */ + public function isunit( $n, $unit ){ + return ($n instanceof Less_Tree_Dimension) && $n->unit->is( ( property_exists($unit,'value') ? $unit->value : $unit) ) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); + } + + /** + * @param string $type + */ + private function _isa($n, $type) { + return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); + } + + public function tint($color, $amount) { + return $this->mix( $this->rgb(255,255,255), $color, $amount); + } + + public function shade($color, $amount) { + return $this->mix($this->rgb(0, 0, 0), $color, $amount); + } + + public function extract($values, $index ){ + $index = (int)$index->value - 1; // (1-based index) + // handle non-array values as an array of length 1 + // return 'undefined' if index is invalid + if( property_exists($values,'value') && is_array($values->value) ){ + if( isset($values->value[$index]) ){ + return $values->value[$index]; + } + return null; + + }elseif( (int)$index === 0 ){ + return $values; + } + + return null; + } + + public function length($values){ + $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1; + return new Less_Tree_Dimension($n); + } + + public function datauri($mimetypeNode, $filePathNode = null ) { + + $filePath = ( $filePathNode ? $filePathNode->value : null ); + $mimetype = $mimetypeNode->value; + + $args = 2; + if( !$filePath ){ + $filePath = $mimetype; + $args = 1; + } + + $filePath = str_replace('\\','/',$filePath); + if( Less_Environment::isPathRelative($filePath) ){ + + if( Less_Parser::$options['relativeUrls'] ){ + $temp = $this->currentFileInfo['currentDirectory']; + } else { + $temp = $this->currentFileInfo['entryPath']; + } + + if( !empty($temp) ){ + $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath); + } + + } + + + // detect the mimetype if not given + if( $args < 2 ){ + + /* incomplete + $mime = require('mime'); + mimetype = mime.lookup(path); + + // use base 64 unless it's an ASCII or UTF-8 format + var charset = mime.charsets.lookup(mimetype); + useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; + if (useBase64) mimetype += ';base64'; + */ + + $mimetype = Less_Mime::lookup($filePath); + + $charset = Less_Mime::charsets_lookup($mimetype); + $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8')); + if( $useBase64 ){ $mimetype .= ';base64'; } + + }else{ + $useBase64 = preg_match('/;base64$/',$mimetype); + } + + + if( file_exists($filePath) ){ + $buf = @file_get_contents($filePath); + }else{ + $buf = false; + } + + + // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded + // and the --ieCompat flag is enabled, return a normal url() instead. + $DATA_URI_MAX_KB = 32; + $fileSizeInKB = round( strlen($buf) / 1024 ); + if( $fileSizeInKB >= $DATA_URI_MAX_KB ){ + $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo); + return $url->compile($this); + } + + if( $buf ){ + $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf); + $filePath = '"data:' . $mimetype . ',' . $buf . '"'; + } + + return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) ); + } + + //svg-gradient + public function svggradient( $direction ){ + + $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]'; + $arguments = func_get_args(); + + if( count($arguments) < 3 ){ + throw new Less_Exception_Compiler( $throw_message ); + } + + $stops = array_slice($arguments,1); + $gradientType = 'linear'; + $rectangleDimension = 'x="0" y="0" width="1" height="1"'; + $useBase64 = true; + $directionValue = $direction->toCSS(); + + + switch( $directionValue ){ + case "to bottom": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"'; + break; + case "to right": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"'; + break; + case "to bottom right": + $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"'; + break; + case "to top right": + $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"'; + break; + case "ellipse": + case "ellipse at center": + $gradientType = "radial"; + $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; + $rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; + break; + default: + throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" ); + } + + $returner = '<?xml version="1.0" ?>' . + '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' . + '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>'; + + for( $i = 0; $i < count($stops); $i++ ){ + if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){ + $color = $stops[$i]->value[0]; + $position = $stops[$i]->value[1]; + }else{ + $color = $stops[$i]; + $position = null; + } + + if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){ + throw new Less_Exception_Compiler( $throw_message ); + } + if( $position ){ + $positionValue = $position->toCSS(); + }elseif( $i === 0 ){ + $positionValue = '0%'; + }else{ + $positionValue = '100%'; + } + $alpha = $color->alpha; + $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>'; + } + + $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>'; + + + if( $useBase64 ){ + $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'"; + }else{ + $returner = "'data:image/svg+xml,".$returner."'"; + } + + return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) ); + } + + + /** + * Php version of javascript's `encodeURIComponent` function + * + * @param string $string The string to encode + * @return string The encoded string + */ + public static function encodeURIComponent($string){ + $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); + return strtr(rawurlencode($string), $revert); + } + + + // Color Blending + // ref: http://www.w3.org/TR/compositing-1 + + public function colorBlend( $mode, $color1, $color2 ){ + $ab = $color1->alpha; // backdrop + $as = $color2->alpha; // source + $r = array(); // result + + $ar = $as + $ab * (1 - $as); + for( $i = 0; $i < 3; $i++ ){ + $cb = $color1->rgb[$i] / 255; + $cs = $color2->rgb[$i] / 255; + $cr = call_user_func( $mode, $cb, $cs ); + if( $ar ){ + $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar; + } + $r[$i] = $cr * 255; + } + + return new Less_Tree_Color($r, $ar); + } + + public function multiply($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 ); + } + + private function colorBlendMultiply($cb, $cs){ + return $cb * $cs; + } + + public function screen($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 ); + } + + private function colorBlendScreen( $cb, $cs){ + return $cb + $cs - $cb * $cs; + } + + public function overlay($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 ); + } + + private function colorBlendOverlay($cb, $cs ){ + $cb *= 2; + return ($cb <= 1) + ? $this->colorBlendMultiply($cb, $cs) + : $this->colorBlendScreen($cb - 1, $cs); + } + + public function softlight($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 ); + } + + private function colorBlendSoftlight($cb, $cs ){ + $d = 1; + $e = $cb; + if( $cs > 0.5 ){ + $e = 1; + $d = ($cb > 0.25) ? sqrt($cb) + : ((16 * $cb - 12) * $cb + 4) * $cb; + } + return $cb - (1 - 2 * $cs) * $e * ($d - $cb); + } + + public function hardlight($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 ); + } + + private function colorBlendHardlight( $cb, $cs ){ + return $this->colorBlendOverlay($cs, $cb); + } + + public function difference($color1 = null, $color2 = null) { + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 ); + } + + private function colorBlendDifference( $cb, $cs ){ + return abs($cb - $cs); + } + + public function exclusion( $color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 ); + } + + private function colorBlendExclusion( $cb, $cs ){ + return $cb + $cs - 2 * $cb * $cs; + } + + public function average($color1 = null, $color2 = null){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 ); + } + + // non-w3c functions: + public function colorBlendAverage($cb, $cs ){ + return ($cb + $cs) / 2; + } + + public function negation($color1 = null, $color2 = null ){ + if (!$color1 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + if (!$color2 instanceof Less_Tree_Color) { + throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); + } + + return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 ); + } + + public function colorBlendNegation($cb, $cs){ + return 1 - abs($cb + $cs - 1); + } + + // ~ End of Color Blending + +} + + +/** + * Mime lookup + * + * @package Less + * @subpackage node + */ +class Less_Mime{ + + // this map is intentionally incomplete + // if you want more, install 'mime' dep + static $_types = array( + '.htm' => 'text/html', + '.html'=> 'text/html', + '.gif' => 'image/gif', + '.jpg' => 'image/jpeg', + '.jpeg'=> 'image/jpeg', + '.png' => 'image/png', + '.ttf' => 'application/x-font-ttf', + '.otf' => 'application/x-font-otf', + '.eot' => 'application/vnd.ms-fontobject', + '.woff' => 'application/x-font-woff', + '.svg' => 'image/svg+xml', + ); + + public static function lookup( $filepath ){ + $parts = explode('.',$filepath); + $ext = '.'.strtolower(array_pop($parts)); + + if( !isset(self::$_types[$ext]) ){ + return null; + } + return self::$_types[$ext]; + } + + public static function charsets_lookup( $type = null ){ + // assumes all text types are UTF-8 + return $type && preg_match('/^text\//',$type) ? 'UTF-8' : ''; + } +} + + +/** + * Tree + * + * @package Less + * @subpackage tree + */ +class Less_Tree{ + + public $cache_string; + + public function toCSS(){ + $output = new Less_Output(); + $this->genCSS($output); + return $output->toString(); + } + + + /** + * Generate CSS by adding it to the output object + * + * @param Less_Output $output The output + * @return void + */ + public function genCSS($output){} + + + /** + * @param Less_Tree_Ruleset[] $rules + */ + public static function outputRuleset( $output, $rules ){ + + $ruleCnt = count($rules); + Less_Environment::$tabLevel++; + + + // Compressed + if( Less_Parser::$options['compress'] ){ + $output->add('{'); + for( $i = 0; $i < $ruleCnt; $i++ ){ + $rules[$i]->genCSS( $output ); + } + + $output->add( '}' ); + Less_Environment::$tabLevel--; + return; + } + + + // Non-compressed + $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 ); + $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation']; + + $output->add( " {" ); + for($i = 0; $i < $ruleCnt; $i++ ){ + $output->add( $tabRuleStr ); + $rules[$i]->genCSS( $output ); + } + Less_Environment::$tabLevel--; + $output->add( $tabSetStr.'}' ); + + } + + public function accept($visitor){} + + + public static function ReferencedArray($rules){ + foreach($rules as $rule){ + if( method_exists($rule, 'markReferenced') ){ + $rule->markReferenced(); + } + } + } + + + /** + * Requires php 5.3+ + */ + public static function __set_state($args){ + + $class = get_called_class(); + $obj = new $class(null,null,null,null); + foreach($args as $key => $val){ + $obj->$key = $val; + } + return $obj; + } + +} + + +/** + * Parser output + * + * @package Less + * @subpackage output + */ +class Less_Output{ + + /** + * Output holder + * + * @var string + */ + protected $strs = array(); + + /** + * Adds a chunk to the stack + * + * @param string $chunk The chunk to output + * @param Less_FileInfo $fileInfo The file information + * @param integer $index The index + * @param mixed $mapLines + */ + public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){ + $this->strs[] = $chunk; + } + + /** + * Is the output empty? + * + * @return boolean + */ + public function isEmpty(){ + return count($this->strs) === 0; + } + + + /** + * Converts the output to string + * + * @return string + */ + public function toString(){ + return implode('',$this->strs); + } + +} + +/** + * Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_Visitor{ + + protected $methods = array(); + protected $_visitFnCache = array(); + + public function __construct(){ + $this->_visitFnCache = get_class_methods(get_class($this)); + $this->_visitFnCache = array_flip($this->_visitFnCache); + } + + public function visitObj( $node ){ + + $funcName = 'visit'.$node->type; + if( isset($this->_visitFnCache[$funcName]) ){ + + $visitDeeper = true; + $this->$funcName( $node, $visitDeeper ); + + if( $visitDeeper ){ + $node->accept($this); + } + + $funcName = $funcName . "Out"; + if( isset($this->_visitFnCache[$funcName]) ){ + $this->$funcName( $node ); + } + + }else{ + $node->accept($this); + } + + return $node; + } + + public function visitArray( $nodes ){ + + array_map( array($this,'visitObj'), $nodes); + return $nodes; + } +} + + + +/** + * Replacing Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_VisitorReplacing extends Less_Visitor{ + + public function visitObj( $node ){ + + $funcName = 'visit'.$node->type; + if( isset($this->_visitFnCache[$funcName]) ){ + + $visitDeeper = true; + $node = $this->$funcName( $node, $visitDeeper ); + + if( $node ){ + if( $visitDeeper && is_object($node) ){ + $node->accept($this); + } + + $funcName = $funcName . "Out"; + if( isset($this->_visitFnCache[$funcName]) ){ + $this->$funcName( $node ); + } + } + + }else{ + $node->accept($this); + } + + return $node; + } + + public function visitArray( $nodes ){ + + $newNodes = array(); + foreach($nodes as $node){ + $evald = $this->visitObj($node); + if( $evald ){ + if( is_array($evald) ){ + self::flatten($evald,$newNodes); + }else{ + $newNodes[] = $evald; + } + } + } + return $newNodes; + } + + public function flatten( $arr, &$out ){ + + foreach($arr as $item){ + if( !is_array($item) ){ + $out[] = $item; + continue; + } + + foreach($item as $nestedItem){ + if( is_array($nestedItem) ){ + self::flatten( $nestedItem, $out); + }else{ + $out[] = $nestedItem; + } + } + } + + return $out; + } + +} + + + + +/** + * Configurable + * + * @package Less + * @subpackage Core + */ +abstract class Less_Configurable { + + /** + * Array of options + * + * @var array + */ + protected $options = array(); + + /** + * Array of default options + * + * @var array + */ + protected $defaultOptions = array(); + + + /** + * Set options + * + * If $options is an object it will be converted into an array by called + * it's toArray method. + * + * @throws Exception + * @param array|object $options + * + */ + public function setOptions($options){ + $options = array_intersect_key($options,$this->defaultOptions); + $this->options = array_merge($this->defaultOptions, $this->options, $options); + } + + + /** + * Get an option value by name + * + * If the option is empty or not set a NULL value will be returned. + * + * @param string $name + * @param mixed $default Default value if confiuration of $name is not present + * @return mixed + */ + public function getOption($name, $default = null){ + if(isset($this->options[$name])){ + return $this->options[$name]; + } + return $default; + } + + + /** + * Set an option + * + * @param string $name + * @param mixed $value + */ + public function setOption($name, $value){ + $this->options[$name] = $value; + } + +} + +/** + * Alpha + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Alpha extends Less_Tree{ + public $value; + public $type = 'Alpha'; + + public function __construct($val){ + $this->value = $val; + } + + //function accept( $visitor ){ + // $this->value = $visitor->visit( $this->value ); + //} + + public function compile($env){ + + if( is_object($this->value) ){ + $this->value = $this->value->compile($env); + } + + return $this; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + $output->add( "alpha(opacity=" ); + + if( is_string($this->value) ){ + $output->add( $this->value ); + }else{ + $this->value->genCSS( $output); + } + + $output->add( ')' ); + } + + public function toCSS(){ + return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")"; + } + + +} + +/** + * Anonymous + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Anonymous extends Less_Tree{ + public $value; + public $quote; + public $index; + public $mapLines; + public $currentFileInfo; + public $type = 'Anonymous'; + + /** + * @param integer $index + * @param boolean $mapLines + */ + public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){ + $this->value = $value; + $this->index = $index; + $this->mapLines = $mapLines; + $this->currentFileInfo = $currentFileInfo; + } + + public function compile(){ + return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines); + } + + public function compare($x){ + if( !is_object($x) ){ + return -1; + } + + $left = $this->toCSS(); + $right = $x->toCSS(); + + if( $left === $right ){ + return 0; + } + + return $left < $right ? -1 : 1; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines ); + } + + public function toCSS(){ + return $this->value; + } + +} + + +/** + * Assignment + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Assignment extends Less_Tree{ + + public $key; + public $value; + public $type = 'Assignment'; + + public function __construct($key, $val) { + $this->key = $key; + $this->value = $val; + } + + public function accept( $visitor ){ + $this->value = $visitor->visitObj( $this->value ); + } + + public function compile($env) { + return new Less_Tree_Assignment( $this->key, $this->value->compile($env)); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->key . '=' ); + $this->value->genCSS( $output ); + } + + public function toCss(){ + return $this->key . '=' . $this->value->toCSS(); + } +} + + +/** + * Attribute + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Attribute extends Less_Tree{ + + public $key; + public $op; + public $value; + public $type = 'Attribute'; + + public function __construct($key, $op, $value){ + $this->key = $key; + $this->op = $op; + $this->value = $value; + } + + public function compile($env){ + + $key_obj = is_object($this->key); + $val_obj = is_object($this->value); + + if( !$key_obj && !$val_obj ){ + return $this; + } + + return new Less_Tree_Attribute( + $key_obj ? $this->key->compile($env) : $this->key , + $this->op, + $val_obj ? $this->value->compile($env) : $this->value); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->toCSS() ); + } + + public function toCSS(){ + $value = $this->key; + + if( $this->op ){ + $value .= $this->op; + $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value); + } + + return '[' . $value . ']'; + } +} + + +/** + * Call + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Call extends Less_Tree{ + public $value; + + protected $name; + protected $args; + protected $index; + protected $currentFileInfo; + public $type = 'Call'; + + public function __construct($name, $args, $index, $currentFileInfo = null ){ + $this->name = $name; + $this->args = $args; + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + } + + public function accept( $visitor ){ + $this->args = $visitor->visitArray( $this->args ); + } + + // + // When evaluating a function call, + // we either find the function in `tree.functions` [1], + // in which case we call it, passing the evaluated arguments, + // or we simply print it out as it appeared originally [2]. + // + // The *functions.js* file contains the built-in functions. + // + // The reason why we evaluate the arguments, is in the case where + // we try to pass a variable to a function, like: `saturate(@color)`. + // The function should receive the value, not the variable. + // + public function compile($env=null){ + $args = array(); + foreach($this->args as $a){ + $args[] = $a->compile($env); + } + + $nameLC = strtolower($this->name); + switch($nameLC){ + case '%': + $nameLC = '_percent'; + break; + + case 'get-unit': + $nameLC = 'getunit'; + break; + + case 'data-uri': + $nameLC = 'datauri'; + break; + + case 'svg-gradient': + $nameLC = 'svggradient'; + break; + } + + $result = null; + if( $nameLC === 'default' ){ + $result = Less_Tree_DefaultFunc::compile(); + + }else{ + + if( method_exists('Less_Functions',$nameLC) ){ // 1. + try { + + $func = new Less_Functions($env, $this->currentFileInfo); + $result = call_user_func_array( array($func,$nameLC),$args); + + } catch (Exception $e) { + throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index); + } + } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) { + try { + $result = call_user_func_array( $env->functions[$nameLC], $args ); + } catch (Exception $e) { + throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index); + } + } + } + + if( $result !== null ){ + return $result; + } + + + return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo ); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + $output->add( $this->name . '(', $this->currentFileInfo, $this->index ); + $args_len = count($this->args); + for($i = 0; $i < $args_len; $i++ ){ + $this->args[$i]->genCSS( $output ); + if( $i + 1 < $args_len ){ + $output->add( ', ' ); + } + } + + $output->add( ')' ); + } + + + //public function toCSS(){ + // return $this->compile()->toCSS(); + //} + +} + + +/** + * Color + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Color extends Less_Tree{ + public $rgb; + public $alpha; + public $isTransparentKeyword; + public $type = 'Color'; + + public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){ + + if( $isTransparentKeyword ){ + $this->rgb = $rgb; + $this->alpha = $a; + $this->isTransparentKeyword = true; + return; + } + + $this->rgb = array(); + if( is_array($rgb) ){ + $this->rgb = $rgb; + }else if( strlen($rgb) == 6 ){ + foreach(str_split($rgb, 2) as $c){ + $this->rgb[] = hexdec($c); + } + }else{ + foreach(str_split($rgb, 1) as $c){ + $this->rgb[] = hexdec($c.$c); + } + } + $this->alpha = is_numeric($a) ? $a : 1; + } + + public function compile(){ + return $this; + } + + public function luma(){ + $r = $this->rgb[0] / 255; + $g = $this->rgb[1] / 255; + $b = $this->rgb[2] / 255; + + $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4); + $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4); + $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4); + + return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->toCSS() ); + } + + public function toCSS( $doNotCompress = false ){ + $compress = Less_Parser::$options['compress'] && !$doNotCompress; + $alpha = Less_Functions::fround( $this->alpha ); + + + // + // If we have some transparency, the only way to represent it + // is via `rgba`. Otherwise, we use the hex representation, + // which has better compatibility with older browsers. + // Values are capped between `0` and `255`, rounded and zero-padded. + // + if( $alpha < 1 ){ + if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){ + return 'transparent'; + } + + $values = array(); + foreach($this->rgb as $c){ + $values[] = Less_Functions::clamp( round($c), 255); + } + $values[] = $alpha; + + $glue = ($compress ? ',' : ', '); + return "rgba(" . implode($glue, $values) . ")"; + }else{ + + $color = $this->toRGB(); + + if( $compress ){ + + // Convert color to short format + if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) { + $color = '#'.$color[1] . $color[3] . $color[5]; + } + } + + return $color; + } + } + + // + // Operations have to be done per-channel, if not, + // channels will spill onto each other. Once we have + // our result, in the form of an integer triplet, + // we create a new Color node to hold the result. + // + + /** + * @param string $op + */ + public function operate( $op, $other) { + $rgb = array(); + $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha; + for ($c = 0; $c < 3; $c++) { + $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]); + } + return new Less_Tree_Color($rgb, $alpha); + } + + public function toRGB(){ + return $this->toHex($this->rgb); + } + + public function toHSL(){ + $r = $this->rgb[0] / 255; + $g = $this->rgb[1] / 255; + $b = $this->rgb[2] / 255; + $a = $this->alpha; + + $max = max($r, $g, $b); + $min = min($r, $g, $b); + $l = ($max + $min) / 2; + $d = $max - $min; + + $h = $s = 0; + if( $max !== $min ){ + $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); + + switch ($max) { + case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; + case $g: $h = ($b - $r) / $d + 2; break; + case $b: $h = ($r - $g) / $d + 4; break; + } + $h /= 6; + } + return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a ); + } + + //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript + public function toHSV() { + $r = $this->rgb[0] / 255; + $g = $this->rgb[1] / 255; + $b = $this->rgb[2] / 255; + $a = $this->alpha; + + $max = max($r, $g, $b); + $min = min($r, $g, $b); + + $v = $max; + + $d = $max - $min; + if ($max === 0) { + $s = 0; + } else { + $s = $d / $max; + } + + $h = 0; + if( $max !== $min ){ + switch($max){ + case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; + case $g: $h = ($b - $r) / $d + 2; break; + case $b: $h = ($r - $g) / $d + 4; break; + } + $h /= 6; + } + return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a ); + } + + public function toARGB(){ + $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb); + return $this->toHex( $argb ); + } + + public function compare($x){ + + if( !property_exists( $x, 'rgb' ) ){ + return -1; + } + + + return ($x->rgb[0] === $this->rgb[0] && + $x->rgb[1] === $this->rgb[1] && + $x->rgb[2] === $this->rgb[2] && + $x->alpha === $this->alpha) ? 0 : -1; + } + + public function toHex( $v ){ + + $ret = '#'; + foreach($v as $c){ + $c = Less_Functions::clamp( Less_Parser::round($c), 255); + if( $c < 16 ){ + $ret .= '0'; + } + $ret .= dechex($c); + } + + return $ret; + } + + + /** + * @param string $keyword + */ + public static function fromKeyword( $keyword ){ + $keyword = strtolower($keyword); + + if( Less_Colors::hasOwnProperty($keyword) ){ + // detect named color + return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1)); + } + + if( $keyword === 'transparent' ){ + return new Less_Tree_Color( array(0, 0, 0), 0, true); + } + } + +} + + +/** + * Comment + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Comment extends Less_Tree{ + + public $value; + public $silent; + public $isReferenced; + public $currentFileInfo; + public $type = 'Comment'; + + public function __construct($value, $silent, $index = null, $currentFileInfo = null ){ + $this->value = $value; + $this->silent = !! $silent; + $this->currentFileInfo = $currentFileInfo; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + //if( $this->debugInfo ){ + //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index); + //} + $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n + } + + public function toCSS(){ + return Less_Parser::$options['compress'] ? '' : $this->value; + } + + public function isSilent(){ + $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) ); + $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value); + return $this->silent || $isReference || $isCompressed; + } + + public function compile(){ + return $this; + } + + public function markReferenced(){ + $this->isReferenced = true; + } + +} + + +/** + * Condition + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Condition extends Less_Tree{ + + public $op; + public $lvalue; + public $rvalue; + public $index; + public $negate; + public $type = 'Condition'; + + public function __construct($op, $l, $r, $i = 0, $negate = false) { + $this->op = trim($op); + $this->lvalue = $l; + $this->rvalue = $r; + $this->index = $i; + $this->negate = $negate; + } + + public function accept($visitor){ + $this->lvalue = $visitor->visitObj( $this->lvalue ); + $this->rvalue = $visitor->visitObj( $this->rvalue ); + } + + public function compile($env) { + $a = $this->lvalue->compile($env); + $b = $this->rvalue->compile($env); + + switch( $this->op ){ + case 'and': + $result = $a && $b; + break; + + case 'or': + $result = $a || $b; + break; + + default: + if( Less_Parser::is_method($a, 'compare') ){ + $result = $a->compare($b); + }elseif( Less_Parser::is_method($b, 'compare') ){ + $result = $b->compare($a); + }else{ + throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index); + } + + switch ($result) { + case -1: + $result = $this->op === '<' || $this->op === '=<' || $this->op === '<='; + break; + + case 0: + $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<='; + break; + + case 1: + $result = $this->op === '>' || $this->op === '>='; + break; + } + break; + } + + return $this->negate ? !$result : $result; + } + +} + + +/** + * DefaultFunc + * + * @package Less + * @subpackage tree + */ +class Less_Tree_DefaultFunc{ + + static $error_; + static $value_; + + public static function compile(){ + if( self::$error_ ){ + throw new Exception(self::$error_); + } + if( self::$value_ !== null ){ + return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); + } + } + + public static function value( $v ){ + self::$value_ = $v; + } + + public static function error( $e ){ + self::$error_ = $e; + } + + public static function reset(){ + self::$value_ = self::$error_ = null; + } +} + +/** + * DetachedRuleset + * + * @package Less + * @subpackage tree + */ +class Less_Tree_DetachedRuleset extends Less_Tree{ + + public $ruleset; + public $frames; + public $type = 'DetachedRuleset'; + + public function __construct( $ruleset, $frames = null ){ + $this->ruleset = $ruleset; + $this->frames = $frames; + } + + public function accept($visitor) { + $this->ruleset = $visitor->visitObj($this->ruleset); + } + + public function compile($env){ + if( $this->frames ){ + $frames = $this->frames; + }else{ + $frames = $env->frames; + } + return new Less_Tree_DetachedRuleset($this->ruleset, $frames); + } + + public function callEval($env) { + if( $this->frames ){ + return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) ); + } + return $this->ruleset->compile( $env ); + } +} + + + +/** + * Dimension + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Dimension extends Less_Tree{ + + public $value; + public $unit; + public $type = 'Dimension'; + + public function __construct($value, $unit = null){ + $this->value = floatval($value); + + if( $unit && ($unit instanceof Less_Tree_Unit) ){ + $this->unit = $unit; + }elseif( $unit ){ + $this->unit = new Less_Tree_Unit( array($unit) ); + }else{ + $this->unit = new Less_Tree_Unit( ); + } + } + + public function accept( $visitor ){ + $this->unit = $visitor->visitObj( $this->unit ); + } + + public function compile(){ + return $this; + } + + public function toColor() { + return new Less_Tree_Color(array($this->value, $this->value, $this->value)); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){ + throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString()); + } + + $value = Less_Functions::fround( $this->value ); + $strValue = (string)$value; + + if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){ + // would be output 1e-6 etc. + $strValue = number_format($strValue,10); + $strValue = preg_replace('/\.?0+$/','', $strValue); + } + + if( Less_Parser::$options['compress'] ){ + // Zero values doesn't need a unit + if( $value === 0 && $this->unit->isLength() ){ + $output->add( $strValue ); + return $strValue; + } + + // Float values doesn't need a leading zero + if( $value > 0 && $value < 1 && $strValue[0] === '0' ){ + $strValue = substr($strValue,1); + } + } + + $output->add( $strValue ); + $this->unit->genCSS( $output ); + } + + public function __toString(){ + return $this->toCSS(); + } + + // In an operation between two Dimensions, + // we default to the first Dimension's unit, + // so `1px + 2em` will yield `3px`. + + /** + * @param string $op + */ + public function operate( $op, $other){ + + $value = Less_Functions::operate( $op, $this->value, $other->value); + $unit = clone $this->unit; + + if( $op === '+' || $op === '-' ){ + + if( !$unit->numerator && !$unit->denominator ){ + $unit->numerator = $other->unit->numerator; + $unit->denominator = $other->unit->denominator; + }elseif( !$other->unit->numerator && !$other->unit->denominator ){ + // do nothing + }else{ + $other = $other->convertTo( $this->unit->usedUnits()); + + if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){ + throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'."); + } + + $value = Less_Functions::operate( $op, $this->value, $other->value); + } + }elseif( $op === '*' ){ + $unit->numerator = array_merge($unit->numerator, $other->unit->numerator); + $unit->denominator = array_merge($unit->denominator, $other->unit->denominator); + sort($unit->numerator); + sort($unit->denominator); + $unit->cancel(); + }elseif( $op === '/' ){ + $unit->numerator = array_merge($unit->numerator, $other->unit->denominator); + $unit->denominator = array_merge($unit->denominator, $other->unit->numerator); + sort($unit->numerator); + sort($unit->denominator); + $unit->cancel(); + } + return new Less_Tree_Dimension( $value, $unit); + } + + public function compare($other) { + if ($other instanceof Less_Tree_Dimension) { + + if( $this->unit->isEmpty() || $other->unit->isEmpty() ){ + $a = $this; + $b = $other; + } else { + $a = $this->unify(); + $b = $other->unify(); + if( $a->unit->compare($b->unit) !== 0 ){ + return -1; + } + } + $aValue = $a->value; + $bValue = $b->value; + + if ($bValue > $aValue) { + return -1; + } elseif ($bValue < $aValue) { + return 1; + } else { + return 0; + } + } else { + return -1; + } + } + + public function unify() { + return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' )); + } + + public function convertTo($conversions) { + $value = $this->value; + $unit = clone $this->unit; + + if( is_string($conversions) ){ + $derivedConversions = array(); + foreach( Less_Tree_UnitConversions::$groups as $i ){ + if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){ + $derivedConversions = array( $i => $conversions); + } + } + $conversions = $derivedConversions; + } + + + foreach($conversions as $groupName => $targetUnit){ + $group = Less_Tree_UnitConversions::${$groupName}; + + //numerator + foreach($unit->numerator as $i => $atomicUnit){ + $atomicUnit = $unit->numerator[$i]; + if( !isset($group[$atomicUnit]) ){ + continue; + } + + $value = $value * ($group[$atomicUnit] / $group[$targetUnit]); + + $unit->numerator[$i] = $targetUnit; + } + + //denominator + foreach($unit->denominator as $i => $atomicUnit){ + $atomicUnit = $unit->denominator[$i]; + if( !isset($group[$atomicUnit]) ){ + continue; + } + + $value = $value / ($group[$atomicUnit] / $group[$targetUnit]); + + $unit->denominator[$i] = $targetUnit; + } + } + + $unit->cancel(); + + return new Less_Tree_Dimension( $value, $unit); + } +} + + +/** + * Directive + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Directive extends Less_Tree{ + + public $name; + public $value; + public $rules; + public $index; + public $isReferenced; + public $currentFileInfo; + public $debugInfo; + public $type = 'Directive'; + + public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){ + $this->name = $name; + $this->value = $value; + if( $rules ){ + $this->rules = $rules; + $this->rules->allowImports = true; + } + + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + $this->debugInfo = $debugInfo; + } + + + public function accept( $visitor ){ + if( $this->rules ){ + $this->rules = $visitor->visitObj( $this->rules ); + } + if( $this->value ){ + $this->value = $visitor->visitObj( $this->value ); + } + } + + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $value = $this->value; + $rules = $this->rules; + $output->add( $this->name, $this->currentFileInfo, $this->index ); + if( $this->value ){ + $output->add(' '); + $this->value->genCSS($output); + } + if( $this->rules ){ + Less_Tree::outputRuleset( $output, array($this->rules)); + } else { + $output->add(';'); + } + } + + public function compile($env){ + + $value = $this->value; + $rules = $this->rules; + if( $value ){ + $value = $value->compile($env); + } + + if( $rules ){ + $rules = $rules->compile($env); + $rules->root = true; + } + + return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo ); + } + + + public function variable($name){ + if( $this->rules ){ + return $this->rules->variable($name); + } + } + + public function find($selector){ + if( $this->rules ){ + return $this->rules->find($selector, $this); + } + } + + //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, + + public function markReferenced(){ + $this->isReferenced = true; + if( $this->rules ){ + Less_Tree::ReferencedArray($this->rules->rules); + } + } + +} + + +/** + * Element + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Element extends Less_Tree{ + + public $combinator = ''; + public $value = ''; + public $index; + public $currentFileInfo; + public $type = 'Element'; + + public $value_is_object = false; + + public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){ + + $this->value = $value; + $this->value_is_object = is_object($value); + + if( $combinator ){ + $this->combinator = $combinator; + } + + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + } + + public function accept( $visitor ){ + if( $this->value_is_object ){ //object or string + $this->value = $visitor->visitObj( $this->value ); + } + } + + public function compile($env){ + + if( Less_Environment::$mixin_stack ){ + return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo ); + } + + if( $this->value_is_object ){ + $this->value = $this->value->compile($env); + } + + return $this; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->toCSS(), $this->currentFileInfo, $this->index ); + } + + public function toCSS(){ + + if( $this->value_is_object ){ + $value = $this->value->toCSS(); + }else{ + $value = $this->value; + } + + + if( $value === '' && $this->combinator && $this->combinator === '&' ){ + return ''; + } + + + return Less_Environment::$_outputMap[$this->combinator] . $value; + } + +} + + +/** + * Expression + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Expression extends Less_Tree{ + + public $value = array(); + public $parens = false; + public $parensInOp = false; + public $type = 'Expression'; + + public function __construct( $value, $parens = null ){ + $this->value = $value; + $this->parens = $parens; + } + + public function accept( $visitor ){ + $this->value = $visitor->visitArray( $this->value ); + } + + public function compile($env) { + + $doubleParen = false; + + if( $this->parens && !$this->parensInOp ){ + Less_Environment::$parensStack++; + } + + $returnValue = null; + if( $this->value ){ + + $count = count($this->value); + + if( $count > 1 ){ + + $ret = array(); + foreach($this->value as $e){ + $ret[] = $e->compile($env); + } + $returnValue = new Less_Tree_Expression($ret); + + }else{ + + if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){ + $doubleParen = true; + } + + $returnValue = $this->value[0]->compile($env); + } + + } else { + $returnValue = $this; + } + + if( $this->parens ){ + if( !$this->parensInOp ){ + Less_Environment::$parensStack--; + + }elseif( !Less_Environment::isMathOn() && !$doubleParen ){ + $returnValue = new Less_Tree_Paren($returnValue); + + } + } + return $returnValue; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $val_len = count($this->value); + for( $i = 0; $i < $val_len; $i++ ){ + $this->value[$i]->genCSS( $output ); + if( $i + 1 < $val_len ){ + $output->add( ' ' ); + } + } + } + + public function throwAwayComments() { + + if( is_array($this->value) ){ + $new_value = array(); + foreach($this->value as $v){ + if( $v instanceof Less_Tree_Comment ){ + continue; + } + $new_value[] = $v; + } + $this->value = $new_value; + } + } +} + + +/** + * Extend + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Extend extends Less_Tree{ + + public $selector; + public $option; + public $index; + public $selfSelectors = array(); + public $allowBefore; + public $allowAfter; + public $firstExtendOnThisSelectorPath; + public $type = 'Extend'; + public $ruleset; + + + public $object_id; + public $parent_ids = array(); + + /** + * @param integer $index + */ + public function __construct($selector, $option, $index){ + static $i = 0; + $this->selector = $selector; + $this->option = $option; + $this->index = $index; + + switch($option){ + case "all": + $this->allowBefore = true; + $this->allowAfter = true; + break; + default: + $this->allowBefore = false; + $this->allowAfter = false; + break; + } + + $this->object_id = $i++; + $this->parent_ids = array($this->object_id); + } + + public function accept( $visitor ){ + $this->selector = $visitor->visitObj( $this->selector ); + } + + public function compile( $env ){ + Less_Parser::$has_extends = true; + $this->selector = $this->selector->compile($env); + return $this; + //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index); + } + + public function findSelfSelectors( $selectors ){ + $selfElements = array(); + + + for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){ + $selectorElements = $selectors[$i]->elements; + // duplicate the logic in genCSS function inside the selector node. + // future TODO - move both logics into the selector joiner visitor + if( $i && $selectorElements && $selectorElements[0]->combinator === "") { + $selectorElements[0]->combinator = ' '; + } + $selfElements = array_merge( $selfElements, $selectors[$i]->elements ); + } + + $this->selfSelectors = array(new Less_Tree_Selector($selfElements)); + } + +} + +/** + * CSS @import node + * + * The general strategy here is that we don't want to wait + * for the parsing to be completed, before we start importing + * the file. That's because in the context of a browser, + * most of the time will be spent waiting for the server to respond. + * + * On creation, we push the import path to our import queue, though + * `import,push`, we also pass it a callback, which it'll call once + * the file has been fetched, and parsed. + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Import extends Less_Tree{ + + public $options; + public $index; + public $path; + public $features; + public $currentFileInfo; + public $css; + public $skip; + public $root; + public $type = 'Import'; + + public function __construct($path, $features, $options, $index, $currentFileInfo = null ){ + $this->options = $options; + $this->index = $index; + $this->path = $path; + $this->features = $features; + $this->currentFileInfo = $currentFileInfo; + + if( is_array($options) ){ + $this->options += array('inline'=>false); + + if( isset($this->options['less']) || $this->options['inline'] ){ + $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline']; + } else { + $pathValue = $this->getPath(); + if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){ + $this->css = true; + } + } + } + } + +// +// The actual import node doesn't return anything, when converted to CSS. +// The reason is that it's used at the evaluation stage, so that the rules +// it imports can be treated like any other rules. +// +// In `eval`, we make sure all Import nodes get evaluated, recursively, so +// we end up with a flat structure, which can easily be imported in the parent +// ruleset. +// + + public function accept($visitor){ + + if( $this->features ){ + $this->features = $visitor->visitObj($this->features); + } + $this->path = $visitor->visitObj($this->path); + + if( !$this->options['inline'] && $this->root ){ + $this->root = $visitor->visit($this->root); + } + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + if( $this->css ){ + + $output->add( '@import ', $this->currentFileInfo, $this->index ); + + $this->path->genCSS( $output ); + if( $this->features ){ + $output->add( ' ' ); + $this->features->genCSS( $output ); + } + $output->add( ';' ); + } + } + + public function toCSS(){ + $features = $this->features ? ' ' . $this->features->toCSS() : ''; + + if ($this->css) { + return "@import " . $this->path->toCSS() . $features . ";\n"; + } else { + return ""; + } + } + + /** + * @return string + */ + public function getPath(){ + if ($this->path instanceof Less_Tree_Quoted) { + $path = $this->path->value; + $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less'; + } else if ($this->path instanceof Less_Tree_URL) { + $path = $this->path->value->value; + }else{ + return null; + } + + //remove query string and fragment + return preg_replace('/[\?#][^\?]*$/','',$path); + } + + public function compileForImport( $env ){ + return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo); + } + + public function compilePath($env) { + $path = $this->path->compile($env); + $rootpath = ''; + if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){ + $rootpath = $this->currentFileInfo['rootpath']; + } + + + if( !($path instanceof Less_Tree_URL) ){ + if( $rootpath ){ + $pathValue = $path->value; + // Add the base path if the import is relative + if( $pathValue && Less_Environment::isPathRelative($pathValue) ){ + $path->value = $this->currentFileInfo['uri_root'].$pathValue; + } + } + $path->value = Less_Environment::normalizePath($path->value); + } + + + + return $path; + } + + public function compile( $env ){ + + $evald = $this->compileForImport($env); + + //get path & uri + $path_and_uri = null; + if( is_callable(Less_Parser::$options['import_callback']) ){ + $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald); + } + + if( !$path_and_uri ){ + $path_and_uri = $evald->PathAndUri(); + } + + if( $path_and_uri ){ + list($full_path, $uri) = $path_and_uri; + }else{ + $full_path = $uri = $evald->getPath(); + } + + + //import once + if( $evald->skip( $full_path, $env) ){ + return array(); + } + + if( $this->options['inline'] ){ + //todo needs to reference css file not import + //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true ); + + Less_Parser::AddParsedFile($full_path); + $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true ); + + if( $this->features ){ + return new Less_Tree_Media( array($contents), $this->features->value ); + } + + return array( $contents ); + } + + // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo)) + if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) { + return array(); + } + + + // css ? + if( $evald->css ){ + $features = ( $evald->features ? $evald->features->compile($env) : null ); + return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index); + } + + + return $this->ParseImport( $full_path, $uri, $env ); + } + + + /** + * Using the import directories, get the full absolute path and uri of the import + * + * @param Less_Tree_Import $evald + */ + public function PathAndUri(){ + + $evald_path = $this->getPath(); + + if( $evald_path ){ + + $import_dirs = array(); + + if( Less_Environment::isPathRelative($evald_path) ){ + //if the path is relative, the file should be in the current directory + $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root']; + + }else{ + //otherwise, the file should be relative to the server root + $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri']; + + //if the user supplied entryPath isn't the actual root + $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = ''; + + } + + // always look in user supplied import directories + $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] ); + + + foreach( $import_dirs as $rootpath => $rooturi){ + if( is_callable($rooturi) ){ + list($path, $uri) = call_user_func($rooturi, $evald_path); + if( is_string($path) ){ + $full_path = $path; + return array( $full_path, $uri ); + } + }elseif( !empty($rootpath) ){ + + + if( $rooturi ){ + if( strpos($evald_path,$rooturi) === 0 ){ + $evald_path = substr( $evald_path, strlen($rooturi) ); + } + } + + $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\'); + + if( file_exists($path) ){ + $full_path = Less_Environment::normalizePath($path); + $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path)); + return array( $full_path, $uri ); + } elseif( file_exists($path.'.less') ){ + $full_path = Less_Environment::normalizePath($path.'.less'); + $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less')); + return array( $full_path, $uri ); + } + } + } + } + } + + + /** + * Parse the import url and return the rules + * + * @return Less_Tree_Media|array + */ + public function ParseImport( $full_path, $uri, $env ){ + + $import_env = clone $env; + if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){ + $import_env->currentFileInfo['reference'] = true; + } + + if( (isset($this->options['multiple']) && $this->options['multiple']) ){ + $import_env->importMultiple = true; + } + + $parser = new Less_Parser($import_env); + $root = $parser->parseFile($full_path, $uri, true); + + + $ruleset = new Less_Tree_Ruleset(array(), $root->rules ); + $ruleset->evalImports($import_env); + + return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules; + } + + + /** + * Should the import be skipped? + * + * @return boolean|null + */ + private function Skip($path, $env){ + + $path = Less_Parser::winPath(realpath($path)); + + if( $path && Less_Parser::FileParsed($path) ){ + + if( isset($this->currentFileInfo['reference']) ){ + return true; + } + + return !isset($this->options['multiple']) && !$env->importMultiple; + } + + } +} + + +/** + * Javascript + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Javascript extends Less_Tree{ + + public $type = 'Javascript'; + public $escaped; + public $expression; + public $index; + + /** + * @param boolean $index + * @param boolean $escaped + */ + public function __construct($string, $index, $escaped){ + $this->escaped = $escaped; + $this->expression = $string; + $this->index = $index; + } + + public function compile(){ + return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */'); + } + +} + + +/** + * Keyword + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Keyword extends Less_Tree{ + + public $value; + public $type = 'Keyword'; + + /** + * @param string $value + */ + public function __construct($value){ + $this->value = $value; + } + + public function compile(){ + return $this; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + if( $this->value === '%') { + throw new Less_Exception_Compiler("Invalid % without number"); + } + + $output->add( $this->value ); + } + + public function compare($other) { + if ($other instanceof Less_Tree_Keyword) { + return $other->value === $this->value ? 0 : 1; + } else { + return -1; + } + } +} + + +/** + * Media + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Media extends Less_Tree{ + + public $features; + public $rules; + public $index; + public $currentFileInfo; + public $isReferenced; + public $type = 'Media'; + + public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){ + + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + + $selectors = $this->emptySelectors(); + + $this->features = new Less_Tree_Value($features); + + $this->rules = array(new Less_Tree_Ruleset($selectors, $value)); + $this->rules[0]->allowImports = true; + } + + public function accept( $visitor ){ + $this->features = $visitor->visitObj($this->features); + $this->rules = $visitor->visitArray($this->rules); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + $output->add( '@media ', $this->currentFileInfo, $this->index ); + $this->features->genCSS( $output ); + Less_Tree::outputRuleset( $output, $this->rules); + + } + + public function compile($env) { + + $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo ); + + $strictMathBypass = false; + if( Less_Parser::$options['strictMath'] === false) { + $strictMathBypass = true; + Less_Parser::$options['strictMath'] = true; + } + + $media->features = $this->features->compile($env); + + if( $strictMathBypass ){ + Less_Parser::$options['strictMath'] = false; + } + + $env->mediaPath[] = $media; + $env->mediaBlocks[] = $media; + + array_unshift($env->frames, $this->rules[0]); + $media->rules = array($this->rules[0]->compile($env)); + array_shift($env->frames); + + array_pop($env->mediaPath); + + return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env); + } + + public function variable($name) { + return $this->rules[0]->variable($name); + } + + public function find($selector) { + return $this->rules[0]->find($selector, $this); + } + + public function emptySelectors(){ + $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo ); + $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) ); + $sels[0]->mediaEmpty = true; + return $sels; + } + + public function markReferenced(){ + $this->rules[0]->markReferenced(); + $this->isReferenced = true; + Less_Tree::ReferencedArray($this->rules[0]->rules); + } + + // evaltop + public function compileTop($env) { + $result = $this; + + if (count($env->mediaBlocks) > 1) { + $selectors = $this->emptySelectors(); + $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks); + $result->multiMedia = true; + } + + $env->mediaBlocks = array(); + $env->mediaPath = array(); + + return $result; + } + + public function compileNested($env) { + $path = array_merge($env->mediaPath, array($this)); + + // Extract the media-query conditions separated with `,` (OR). + foreach ($path as $key => $p) { + $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features; + $path[$key] = is_array($value) ? $value : array($value); + } + + // Trace all permutations to generate the resulting media-query. + // + // (a, b and c) with nested (d, e) -> + // a and d + // a and e + // b and c and d + // b and c and e + + $permuted = $this->permute($path); + $expressions = array(); + foreach($permuted as $path){ + + for( $i=0, $len=count($path); $i < $len; $i++){ + $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]); + } + + for( $i = count($path) - 1; $i > 0; $i-- ){ + array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and'))); + } + + $expressions[] = new Less_Tree_Expression($path); + } + $this->features = new Less_Tree_Value($expressions); + + + + // Fake a tree-node that doesn't output anything. + return new Less_Tree_Ruleset(array(), array()); + } + + public function permute($arr) { + if (!$arr) + return array(); + + if (count($arr) == 1) + return $arr[0]; + + $result = array(); + $rest = $this->permute(array_slice($arr, 1)); + foreach ($rest as $r) { + foreach ($arr[0] as $a) { + $result[] = array_merge( + is_array($a) ? $a : array($a), + is_array($r) ? $r : array($r) + ); + } + } + + return $result; + } + + public function bubbleSelectors($selectors) { + + if( !$selectors) return; + + $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0]))); + } + +} + + +/** + * A simple css name-value pair + * ex: width:100px; + * + * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule) + * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000; + * + * @package Less + * @subpackage tree + */ +class Less_Tree_NameValue extends Less_Tree{ + + public $name; + public $value; + public $index; + public $currentFileInfo; + public $type = 'NameValue'; + public $important = ''; + + public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){ + $this->name = $name; + $this->value = $value; + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + } + + public function genCSS( $output ){ + + $output->add( + $this->name + . Less_Environment::$_outputMap[': '] + . $this->value + . $this->important + . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";") + , $this->currentFileInfo, $this->index); + } + + public function compile ($env){ + return $this; + } + + public function makeImportant(){ + $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo); + $new->important = ' !important'; + return $new; + } + + +} + + +/** + * Negative + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Negative extends Less_Tree{ + + public $value; + public $type = 'Negative'; + + public function __construct($node){ + $this->value = $node; + } + + //function accept($visitor) { + // $this->value = $visitor->visit($this->value); + //} + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( '-' ); + $this->value->genCSS( $output ); + } + + public function compile($env) { + if( Less_Environment::isMathOn() ){ + $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) ); + return $ret->compile($env); + } + return new Less_Tree_Negative( $this->value->compile($env) ); + } +} + +/** + * Operation + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Operation extends Less_Tree{ + + public $op; + public $operands; + public $isSpaced; + public $type = 'Operation'; + + /** + * @param string $op + */ + public function __construct($op, $operands, $isSpaced = false){ + $this->op = trim($op); + $this->operands = $operands; + $this->isSpaced = $isSpaced; + } + + public function accept($visitor) { + $this->operands = $visitor->visitArray($this->operands); + } + + public function compile($env){ + $a = $this->operands[0]->compile($env); + $b = $this->operands[1]->compile($env); + + + if( Less_Environment::isMathOn() ){ + + if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){ + $a = $a->toColor(); + + }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){ + $b = $b->toColor(); + + } + + if( !method_exists($a,'operate') ){ + throw new Less_Exception_Compiler("Operation on an invalid type"); + } + + return $a->operate( $this->op, $b); + } + + return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced ); + } + + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $this->operands[0]->genCSS( $output ); + if( $this->isSpaced ){ + $output->add( " " ); + } + $output->add( $this->op ); + if( $this->isSpaced ){ + $output->add( ' ' ); + } + $this->operands[1]->genCSS( $output ); + } + +} + + +/** + * Paren + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Paren extends Less_Tree{ + + public $value; + public $type = 'Paren'; + + public function __construct($value) { + $this->value = $value; + } + + public function accept($visitor){ + $this->value = $visitor->visitObj($this->value); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( '(' ); + $this->value->genCSS( $output ); + $output->add( ')' ); + } + + public function compile($env) { + return new Less_Tree_Paren($this->value->compile($env)); + } + +} + + +/** + * Quoted + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Quoted extends Less_Tree{ + public $escaped; + public $value; + public $quote; + public $index; + public $currentFileInfo; + public $type = 'Quoted'; + + /** + * @param string $str + */ + public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){ + $this->escaped = $escaped; + $this->value = $content; + if( $str ){ + $this->quote = $str[0]; + } + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + if( !$this->escaped ){ + $output->add( $this->quote, $this->currentFileInfo, $this->index ); + } + $output->add( $this->value ); + if( !$this->escaped ){ + $output->add( $this->quote ); + } + } + + public function compile($env){ + + $value = $this->value; + if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){ + foreach($matches as $i => $match){ + $js = new Less_Tree_JavaScript($matches[1], $this->index, true); + $js = $js->compile()->value; + $value = str_replace($matches[0][$i], $js, $value); + } + } + + if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){ + foreach($matches[1] as $i => $match){ + $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo ); + $v = $v->compile($env); + $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS(); + $value = str_replace($matches[0][$i], $v, $value); + } + } + + return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo); + } + + public function compare($x) { + + if( !Less_Parser::is_method($x, 'toCSS') ){ + return -1; + } + + $left = $this->toCSS(); + $right = $x->toCSS(); + + if ($left === $right) { + return 0; + } + + return $left < $right ? -1 : 1; + } +} + + +/** + * Rule + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Rule extends Less_Tree{ + + public $name; + public $value; + public $important; + public $merge; + public $index; + public $inline; + public $variable; + public $currentFileInfo; + public $type = 'Rule'; + + /** + * @param string $important + */ + public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false){ + $this->name = $name; + $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value)); + $this->important = $important ? ' ' . trim($important) : ''; + $this->merge = $merge; + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + $this->inline = $inline; + $this->variable = ( is_string($name) && $name[0] === '@'); + } + + public function accept($visitor) { + $this->value = $visitor->visitObj( $this->value ); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index); + try{ + $this->value->genCSS( $output); + + }catch( Less_Exception_Parser $e ){ + $e->index = $this->index; + $e->currentFile = $this->currentFileInfo; + throw $e; + } + $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index); + } + + public function compile ($env){ + + $name = $this->name; + if( is_array($name) ){ + // expand 'primitive' name directly to get + // things faster (~10% for benchmark.less): + if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){ + $name = $name[0]->value; + }else{ + $name = $this->CompileName($env,$name); + } + } + + $strictMathBypass = Less_Parser::$options['strictMath']; + if( $name === "font" && !Less_Parser::$options['strictMath'] ){ + Less_Parser::$options['strictMath'] = true; + } + + try { + $evaldValue = $this->value->compile($env); + + if( !$this->variable && $evaldValue->type === "DetachedRuleset") { + throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo); + } + + if( Less_Environment::$mixin_stack ){ + $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline); + }else{ + $this->name = $name; + $this->value = $evaldValue; + $return = $this; + } + + }catch( Less_Exception_Parser $e ){ + if( !is_numeric($e->index) ){ + $e->index = $this->index; + $e->currentFile = $this->currentFileInfo; + } + throw $e; + } + + Less_Parser::$options['strictMath'] = $strictMathBypass; + + return $return; + } + + + public function CompileName( $env, $name ){ + $output = new Less_Output(); + foreach($name as $n){ + $n->compile($env)->genCSS($output); + } + return $output->toString(); + } + + public function makeImportant(){ + return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline); + } + +} + + +/** + * Ruleset + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Ruleset extends Less_Tree{ + + protected $lookups; + public $_variables; + public $_rulesets; + + public $strictImports; + + public $selectors; + public $rules; + public $root; + public $allowImports; + public $paths; + public $firstRoot; + public $type = 'Ruleset'; + public $multiMedia; + public $allExtends; + + public $ruleset_id; + public $originalRuleset; + + public $first_oelements; + + public function SetRulesetIndex(){ + $this->ruleset_id = Less_Parser::$next_id++; + $this->originalRuleset = $this->ruleset_id; + + if( $this->selectors ){ + foreach($this->selectors as $sel){ + if( $sel->_oelements ){ + $this->first_oelements[$sel->_oelements[0]] = true; + } + } + } + } + + public function __construct($selectors, $rules, $strictImports = null){ + $this->selectors = $selectors; + $this->rules = $rules; + $this->lookups = array(); + $this->strictImports = $strictImports; + $this->SetRulesetIndex(); + } + + public function accept( $visitor ){ + if( $this->paths ){ + $paths_len = count($this->paths); + for($i = 0,$paths_len; $i < $paths_len; $i++ ){ + $this->paths[$i] = $visitor->visitArray($this->paths[$i]); + } + }elseif( $this->selectors ){ + $this->selectors = $visitor->visitArray($this->selectors); + } + + if( $this->rules ){ + $this->rules = $visitor->visitArray($this->rules); + } + } + + public function compile($env){ + + $ruleset = $this->PrepareRuleset($env); + + + // Store the frames around mixin definitions, + // so they can be evaluated like closures when the time comes. + $rsRuleCnt = count($ruleset->rules); + for( $i = 0; $i < $rsRuleCnt; $i++ ){ + if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){ + $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); + } + } + + $mediaBlockCount = 0; + if( $env instanceof Less_Environment ){ + $mediaBlockCount = count($env->mediaBlocks); + } + + // Evaluate mixin calls. + $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt ); + + + // Evaluate everything else + for( $i=0; $i<$rsRuleCnt; $i++ ){ + if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){ + $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); + } + } + + // Evaluate everything else + for( $i=0; $i<$rsRuleCnt; $i++ ){ + $rule = $ruleset->rules[$i]; + + // for rulesets, check if it is a css guard and can be removed + if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){ + + // check if it can be folded in (e.g. & where) + if( $rule->selectors[0]->isJustParentSelector() ){ + array_splice($ruleset->rules,$i--,1); + $rsRuleCnt--; + + for($j = 0; $j < count($rule->rules); $j++ ){ + $subRule = $rule->rules[$j]; + if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){ + array_splice($ruleset->rules, ++$i, 0, array($subRule)); + $rsRuleCnt++; + } + } + + } + } + } + + + // Pop the stack + $env->shiftFrame(); + + if ($mediaBlockCount) { + $len = count($env->mediaBlocks); + for($i = $mediaBlockCount; $i < $len; $i++ ){ + $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors); + } + } + + return $ruleset; + } + + /** + * Compile Less_Tree_Mixin_Call objects + * + * @param Less_Tree_Ruleset $ruleset + * @param integer $rsRuleCnt + */ + private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){ + for($i=0; $i < $rsRuleCnt; $i++){ + $rule = $ruleset->rules[$i]; + + if( $rule instanceof Less_Tree_Mixin_Call ){ + $rule = $rule->compile($env); + + $temp = array(); + foreach($rule as $r){ + if( ($r instanceof Less_Tree_Rule) && $r->variable ){ + // do not pollute the scope if the variable is + // already there. consider returning false here + // but we need a way to "return" variable from mixins + if( !$ruleset->variable($r->name) ){ + $temp[] = $r; + } + }else{ + $temp[] = $r; + } + } + $temp_count = count($temp)-1; + array_splice($ruleset->rules, $i, 1, $temp); + $rsRuleCnt += $temp_count; + $i += $temp_count; + $ruleset->resetCache(); + + }elseif( $rule instanceof Less_Tree_RulesetCall ){ + + $rule = $rule->compile($env); + $rules = array(); + foreach($rule->rules as $r){ + if( ($r instanceof Less_Tree_Rule) && $r->variable ){ + continue; + } + $rules[] = $r; + } + + array_splice($ruleset->rules, $i, 1, $rules); + $temp_count = count($rules); + $rsRuleCnt += $temp_count - 1; + $i += $temp_count-1; + $ruleset->resetCache(); + } + + } + } + + + /** + * Compile the selectors and create a new ruleset object for the compile() method + * + */ + private function PrepareRuleset($env){ + + $hasOnePassingSelector = false; + $selectors = array(); + if( $this->selectors ){ + Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,"); + + foreach($this->selectors as $s){ + $selector = $s->compile($env); + $selectors[] = $selector; + if( $selector->evaldCondition ){ + $hasOnePassingSelector = true; + } + } + + Less_Tree_DefaultFunc::reset(); + } else { + $hasOnePassingSelector = true; + } + + if( $this->rules && $hasOnePassingSelector ){ + $rules = $this->rules; + }else{ + $rules = array(); + } + + $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports); + + $ruleset->originalRuleset = $this->ruleset_id; + + $ruleset->root = $this->root; + $ruleset->firstRoot = $this->firstRoot; + $ruleset->allowImports = $this->allowImports; + + + // push the current ruleset to the frames stack + $env->unshiftFrame($ruleset); + + + // Evaluate imports + if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){ + $ruleset->evalImports($env); + } + + return $ruleset; + } + + function evalImports($env) { + + $rules_len = count($this->rules); + for($i=0; $i < $rules_len; $i++){ + $rule = $this->rules[$i]; + + if( $rule instanceof Less_Tree_Import ){ + $rules = $rule->compile($env); + if( is_array($rules) ){ + array_splice($this->rules, $i, 1, $rules); + $temp_count = count($rules)-1; + $i += $temp_count; + $rules_len += $temp_count; + }else{ + array_splice($this->rules, $i, 1, array($rules)); + } + + $this->resetCache(); + } + } + } + + function makeImportant(){ + + $important_rules = array(); + foreach($this->rules as $rule){ + if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){ + $important_rules[] = $rule->makeImportant(); + }else{ + $important_rules[] = $rule; + } + } + + return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports ); + } + + public function matchArgs($args){ + return !$args; + } + + // lets you call a css selector with a guard + public function matchCondition( $args, $env ){ + $lastSelector = end($this->selectors); + + if( !$lastSelector->evaldCondition ){ + return false; + } + if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){ + return false; + } + return true; + } + + function resetCache(){ + $this->_rulesets = null; + $this->_variables = null; + $this->lookups = array(); + } + + public function variables(){ + $this->_variables = array(); + foreach( $this->rules as $r){ + if ($r instanceof Less_Tree_Rule && $r->variable === true) { + $this->_variables[$r->name] = $r; + } + } + } + + public function variable($name){ + + if( is_null($this->_variables) ){ + $this->variables(); + } + return isset($this->_variables[$name]) ? $this->_variables[$name] : null; + } + + public function find( $selector, $self = null ){ + + $key = implode(' ',$selector->_oelements); + + if( !isset($this->lookups[$key]) ){ + + if( !$self ){ + $self = $this->ruleset_id; + } + + $this->lookups[$key] = array(); + + $first_oelement = $selector->_oelements[0]; + + foreach($this->rules as $rule){ + if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){ + + if( isset($rule->first_oelements[$first_oelement]) ){ + + foreach( $rule->selectors as $ruleSelector ){ + $match = $selector->match($ruleSelector); + if( $match ){ + if( $selector->elements_len > $match ){ + $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self)); + } else { + $this->lookups[$key][] = $rule; + } + break; + } + } + } + } + } + } + + return $this->lookups[$key]; + } + + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + if( !$this->root ){ + Less_Environment::$tabLevel++; + } + + $tabRuleStr = $tabSetStr = ''; + if( !Less_Parser::$options['compress'] ){ + if( Less_Environment::$tabLevel ){ + $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel ); + $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 ); + }else{ + $tabSetStr = $tabRuleStr = "\n"; + } + } + + + $ruleNodes = array(); + $rulesetNodes = array(); + foreach($this->rules as $rule){ + + $class = get_class($rule); + if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){ + $rulesetNodes[] = $rule; + }else{ + $ruleNodes[] = $rule; + } + } + + // If this is the root node, we don't render + // a selector, or {}. + if( !$this->root ){ + + /* + debugInfo = tree.debugInfo(env, this, tabSetStr); + + if (debugInfo) { + output.add(debugInfo); + output.add(tabSetStr); + } + */ + + $paths_len = count($this->paths); + for( $i = 0; $i < $paths_len; $i++ ){ + $path = $this->paths[$i]; + $firstSelector = true; + + foreach($path as $p){ + $p->genCSS( $output, $firstSelector ); + $firstSelector = false; + } + + if( $i + 1 < $paths_len ){ + $output->add( ',' . $tabSetStr ); + } + } + + $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr ); + } + + // Compile rules and rulesets + $ruleNodes_len = count($ruleNodes); + $rulesetNodes_len = count($rulesetNodes); + for( $i = 0; $i < $ruleNodes_len; $i++ ){ + $rule = $ruleNodes[$i]; + + // @page{ directive ends up with root elements inside it, a mix of rules and rulesets + // In this instance we do not know whether it is the last property + if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){ + Less_Environment::$lastRule = true; + } + + $rule->genCSS( $output ); + + if( !Less_Environment::$lastRule ){ + $output->add( $tabRuleStr ); + }else{ + Less_Environment::$lastRule = false; + } + } + + if( !$this->root ){ + $output->add( $tabSetStr . '}' ); + Less_Environment::$tabLevel--; + } + + $firstRuleset = true; + $space = ($this->root ? $tabRuleStr : $tabSetStr); + for( $i = 0; $i < $rulesetNodes_len; $i++ ){ + + if( $ruleNodes_len && $firstRuleset ){ + $output->add( $space ); + }elseif( !$firstRuleset ){ + $output->add( $space ); + } + $firstRuleset = false; + $rulesetNodes[$i]->genCSS( $output); + } + + if( !Less_Parser::$options['compress'] && $this->firstRoot ){ + $output->add( "\n" ); + } + + } + + + function markReferenced(){ + if( !$this->selectors ){ + return; + } + foreach($this->selectors as $selector){ + $selector->markReferenced(); + } + } + + public function joinSelectors( $context, $selectors ){ + $paths = array(); + if( is_array($selectors) ){ + foreach($selectors as $selector) { + $this->joinSelector( $paths, $context, $selector); + } + } + return $paths; + } + + public function joinSelector( &$paths, $context, $selector){ + + $hasParentSelector = false; + + foreach($selector->elements as $el) { + if( $el->value === '&') { + $hasParentSelector = true; + } + } + + if( !$hasParentSelector ){ + if( $context ){ + foreach($context as $context_el){ + $paths[] = array_merge($context_el, array($selector) ); + } + }else { + $paths[] = array($selector); + } + return; + } + + + // The paths are [[Selector]] + // The first list is a list of comma seperated selectors + // The inner list is a list of inheritance seperated selectors + // e.g. + // .a, .b { + // .c { + // } + // } + // == [[.a] [.c]] [[.b] [.c]] + // + + // the elements from the current selector so far + $currentElements = array(); + // the current list of new selectors to add to the path. + // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors + // by the parents + $newSelectors = array(array()); + + + foreach( $selector->elements as $el){ + + // non parent reference elements just get added + if( $el->value !== '&' ){ + $currentElements[] = $el; + } else { + // the new list of selectors to add + $selectorsMultiplied = array(); + + // merge the current list of non parent selector elements + // on to the current list of selectors to add + if( $currentElements ){ + $this->mergeElementsOnToSelectors( $currentElements, $newSelectors); + } + + // loop through our current selectors + foreach($newSelectors as $sel){ + + // if we don't have any parent paths, the & might be in a mixin so that it can be used + // whether there are parents or not + if( !$context ){ + // the combinator used on el should now be applied to the next element instead so that + // it is not lost + if( $sel ){ + $sel[0]->elements = array_slice($sel[0]->elements,0); + $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo ); + } + $selectorsMultiplied[] = $sel; + }else { + + // and the parent selectors + foreach($context as $parentSel){ + // We need to put the current selectors + // then join the last selector's elements on to the parents selectors + + // our new selector path + $newSelectorPath = array(); + // selectors from the parent after the join + $afterParentJoin = array(); + $newJoinedSelectorEmpty = true; + + //construct the joined selector - if & is the first thing this will be empty, + // if not newJoinedSelector will be the last set of elements in the selector + if( $sel ){ + $newSelectorPath = $sel; + $lastSelector = array_pop($newSelectorPath); + $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) ); + $newJoinedSelectorEmpty = false; + } + else { + $newJoinedSelector = $selector->createDerived(array()); + } + + //put together the parent selectors after the join + if ( count($parentSel) > 1) { + $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) ); + } + + if ( $parentSel ){ + $newJoinedSelectorEmpty = false; + + // join the elements so far with the first part of the parent + $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo); + + $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) ); + } + + if (!$newJoinedSelectorEmpty) { + // now add the joined selector + $newSelectorPath[] = $newJoinedSelector; + } + + // and the rest of the parent + $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin); + + // add that to our new set of selectors + $selectorsMultiplied[] = $newSelectorPath; + } + } + } + + // our new selectors has been multiplied, so reset the state + $newSelectors = $selectorsMultiplied; + $currentElements = array(); + } + } + + // if we have any elements left over (e.g. .a& .b == .b) + // add them on to all the current selectors + if( $currentElements ){ + $this->mergeElementsOnToSelectors($currentElements, $newSelectors); + } + foreach( $newSelectors as $new_sel){ + if( $new_sel ){ + $paths[] = $new_sel; + } + } + } + + function mergeElementsOnToSelectors( $elements, &$selectors){ + + if( !$selectors ){ + $selectors[] = array( new Less_Tree_Selector($elements) ); + return; + } + + + foreach( $selectors as &$sel){ + + // if the previous thing in sel is a parent this needs to join on to it + if( $sel ){ + $last = count($sel)-1; + $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) ); + }else{ + $sel[] = new Less_Tree_Selector( $elements ); + } + } + } +} + + +/** + * RulesetCall + * + * @package Less + * @subpackage tree + */ +class Less_Tree_RulesetCall extends Less_Tree{ + + public $variable; + public $type = "RulesetCall"; + + public function __construct($variable){ + $this->variable = $variable; + } + + public function accept($visitor) {} + + public function compile( $env ){ + $variable = new Less_Tree_Variable($this->variable); + $detachedRuleset = $variable->compile($env); + return $detachedRuleset->callEval($env); + } +} + + + +/** + * Selector + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Selector extends Less_Tree{ + + public $elements; + public $condition; + public $extendList = array(); + public $_css; + public $index; + public $evaldCondition = false; + public $type = 'Selector'; + public $currentFileInfo = array(); + public $isReferenced; + public $mediaEmpty; + + public $elements_len = 0; + + public $_oelements; + public $_oelements_len; + public $cacheable = true; + + /** + * @param boolean $isReferenced + */ + public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){ + + $this->elements = $elements; + $this->elements_len = count($elements); + $this->extendList = $extendList; + $this->condition = $condition; + if( $currentFileInfo ){ + $this->currentFileInfo = $currentFileInfo; + } + $this->isReferenced = $isReferenced; + if( !$condition ){ + $this->evaldCondition = true; + } + + $this->CacheElements(); + } + + public function accept($visitor) { + $this->elements = $visitor->visitArray($this->elements); + $this->extendList = $visitor->visitArray($this->extendList); + if( $this->condition ){ + $this->condition = $visitor->visitObj($this->condition); + } + + if( $visitor instanceof Less_Visitor_extendFinder ){ + $this->CacheElements(); + } + } + + public function createDerived( $elements, $extendList = null, $evaldCondition = null ){ + $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced); + $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition; + return $newSelector; + } + + + public function match( $other ){ + + if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){ + return 0; + } + + for( $i = 0; $i < $other->_oelements_len; $i++ ){ + if( $this->elements[$i]->value !== $other->_oelements[$i]) { + return 0; + } + } + + return $other->_oelements_len; // return number of matched elements + } + + + public function CacheElements(){ + + $this->_oelements = array(); + $css = ''; + + foreach($this->elements as $v){ + + $css .= $v->combinator; + if( !$v->value_is_object ){ + $css .= $v->value; + continue; + } + + if( !property_exists($v->value,'value') || !is_string($v->value->value) ){ + $this->cacheable = false; + return; + } + $css .= $v->value->value; + } + + $this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches); + if( $this->_oelements_len ){ + $this->_oelements = $matches[0]; + + if( $this->_oelements[0] === '&' ){ + array_shift($this->_oelements); + $this->_oelements_len--; + } + } + } + + public function isJustParentSelector(){ + return !$this->mediaEmpty && + count($this->elements) === 1 && + $this->elements[0]->value === '&' && + ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === ''); + } + + public function compile($env) { + + $elements = array(); + foreach($this->elements as $el){ + $elements[] = $el->compile($env); + } + + $extendList = array(); + foreach($this->extendList as $el){ + $extendList[] = $el->compile($el); + } + + $evaldCondition = false; + if( $this->condition ){ + $evaldCondition = $this->condition->compile($env); + } + + return $this->createDerived( $elements, $extendList, $evaldCondition ); + } + + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output, $firstSelector = true ){ + + if( !$firstSelector && $this->elements[0]->combinator === "" ){ + $output->add(' ', $this->currentFileInfo, $this->index); + } + + foreach($this->elements as $element){ + $element->genCSS( $output ); + } + } + + public function markReferenced(){ + $this->isReferenced = true; + } + + public function getIsReferenced(){ + return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced; + } + + public function getIsOutput(){ + return $this->evaldCondition; + } + +} + + +/** + * UnicodeDescriptor + * + * @package Less + * @subpackage tree + */ +class Less_Tree_UnicodeDescriptor extends Less_Tree{ + + public $value; + public $type = 'UnicodeDescriptor'; + + public function __construct($value){ + $this->value = $value; + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( $this->value ); + } + + public function compile(){ + return $this; + } +} + + + +/** + * Unit + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Unit extends Less_Tree{ + + var $numerator = array(); + var $denominator = array(); + public $backupUnit; + public $type = 'Unit'; + + public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){ + $this->numerator = $numerator; + $this->denominator = $denominator; + $this->backupUnit = $backupUnit; + } + + public function __clone(){ + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + + if( $this->numerator ){ + $output->add( $this->numerator[0] ); + }elseif( $this->denominator ){ + $output->add( $this->denominator[0] ); + }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){ + $output->add( $this->backupUnit ); + return ; + } + } + + public function toString(){ + $returnStr = implode('*',$this->numerator); + foreach($this->denominator as $d){ + $returnStr .= '/'.$d; + } + return $returnStr; + } + + public function __toString(){ + return $this->toString(); + } + + + /** + * @param Less_Tree_Unit $other + */ + public function compare($other) { + return $this->is( $other->toString() ) ? 0 : -1; + } + + public function is($unitString){ + return $this->toString() === $unitString; + } + + public function isLength(){ + $css = $this->toCSS(); + return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css); + } + + public function isAngle() { + return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] ); + } + + public function isEmpty(){ + return !$this->numerator && !$this->denominator; + } + + public function isSingular() { + return count($this->numerator) <= 1 && !$this->denominator; + } + + + public function usedUnits(){ + $result = array(); + + foreach(Less_Tree_UnitConversions::$groups as $groupName){ + $group = Less_Tree_UnitConversions::${$groupName}; + + foreach($this->numerator as $atomicUnit){ + if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){ + $result[$groupName] = $atomicUnit; + } + } + + foreach($this->denominator as $atomicUnit){ + if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){ + $result[$groupName] = $atomicUnit; + } + } + } + + return $result; + } + + public function cancel(){ + $counter = array(); + $backup = null; + + foreach($this->numerator as $atomicUnit){ + if( !$backup ){ + $backup = $atomicUnit; + } + $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1; + } + + foreach($this->denominator as $atomicUnit){ + if( !$backup ){ + $backup = $atomicUnit; + } + $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1; + } + + $this->numerator = array(); + $this->denominator = array(); + + foreach($counter as $atomicUnit => $count){ + if( $count > 0 ){ + for( $i = 0; $i < $count; $i++ ){ + $this->numerator[] = $atomicUnit; + } + }elseif( $count < 0 ){ + for( $i = 0; $i < -$count; $i++ ){ + $this->denominator[] = $atomicUnit; + } + } + } + + if( !$this->numerator && !$this->denominator && $backup ){ + $this->backupUnit = $backup; + } + + sort($this->numerator); + sort($this->denominator); + } + + +} + + + +/** + * UnitConversions + * + * @package Less + * @subpackage tree + */ +class Less_Tree_UnitConversions{ + + public static $groups = array('length','duration','angle'); + + public static $length = array( + 'm'=> 1, + 'cm'=> 0.01, + 'mm'=> 0.001, + 'in'=> 0.0254, + 'px'=> 0.000264583, // 0.0254 / 96, + 'pt'=> 0.000352778, // 0.0254 / 72, + 'pc'=> 0.004233333, // 0.0254 / 72 * 12 + ); + + public static $duration = array( + 's'=> 1, + 'ms'=> 0.001 + ); + + public static $angle = array( + 'rad' => 0.1591549430919, // 1/(2*M_PI), + 'deg' => 0.002777778, // 1/360, + 'grad'=> 0.0025, // 1/400, + 'turn'=> 1 + ); + +} + +/** + * Url + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Url extends Less_Tree{ + + public $attrs; + public $value; + public $currentFileInfo; + public $isEvald; + public $type = 'Url'; + + public function __construct($value, $currentFileInfo = null, $isEvald = null){ + $this->value = $value; + $this->currentFileInfo = $currentFileInfo; + $this->isEvald = $isEvald; + } + + public function accept( $visitor ){ + $this->value = $visitor->visitObj($this->value); + } + + /** + * @see Less_Tree::genCSS + */ + public function genCSS( $output ){ + $output->add( 'url(' ); + $this->value->genCSS( $output ); + $output->add( ')' ); + } + + /** + * @param Less_Functions $ctx + */ + public function compile($ctx){ + $val = $this->value->compile($ctx); + + if( !$this->isEvald ){ + // Add the base path if the URL is relative + if( Less_Parser::$options['relativeUrls'] + && $this->currentFileInfo + && is_string($val->value) + && Less_Environment::isPathRelative($val->value) + ){ + $rootpath = $this->currentFileInfo['uri_root']; + if ( !$val->quote ){ + $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath ); + } + $val->value = $rootpath . $val->value; + } + + $val->value = Less_Environment::normalizePath( $val->value); + } + + // Add cache buster if enabled + if( Less_Parser::$options['urlArgs'] ){ + if( !preg_match('/^\s*data:/',$val->value) ){ + $delimiter = strpos($val->value,'?') === false ? '?' : '&'; + $urlArgs = $delimiter . Less_Parser::$options['urlArgs']; + $hash_pos = strpos($val->value,'#'); + if( $hash_pos !== false ){ + $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0); + } else { + $val->value .= $urlArgs; + } + } + } + + return new Less_Tree_URL($val, $this->currentFileInfo, true); + } + +} + + +/** + * Value + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Value extends Less_Tree{ + + public $type = 'Value'; + public $value; + + public function __construct($value){ + $this->value = $value; + } + + public function accept($visitor) { + $this->value = $visitor->visitArray($this->value); + } + + public function compile($env){ + + $ret = array(); + $i = 0; + foreach($this->value as $i => $v){ + $ret[] = $v->compile($env); + } + if( $i > 0 ){ + return new Less_Tree_Value($ret); + } + return $ret[0]; + } + + /** + * @see Less_Tree::genCSS + */ + function genCSS( $output ){ + $len = count($this->value); + for($i = 0; $i < $len; $i++ ){ + $this->value[$i]->genCSS( $output ); + if( $i+1 < $len ){ + $output->add( Less_Environment::$_outputMap[','] ); + } + } + } + +} + + +/** + * Variable + * + * @package Less + * @subpackage tree + */ +class Less_Tree_Variable extends Less_Tree{ + + public $name; + public $index; + public $currentFileInfo; + public $evaluating = false; + public $type = 'Variable'; + + /** + * @param string $name + */ + public function __construct($name, $index = null, $currentFileInfo = null) { + $this->name = $name; + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + } + + public function compile($env) { + + if( $this->name[1] === '@' ){ + $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo); + $name = '@' . $v->compile($env)->value; + }else{ + $name = $this->name; + } + + if ($this->evaluating) { + throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo); + } + + $this->evaluating = true; + + foreach($env->frames as $frame){ + if( $v = $frame->variable($name) ){ + $r = $v->value->compile($env); + $this->evaluating = false; + return $r; + } + } + + throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo); + } + +} + + + +class Less_Tree_Mixin_Call extends Less_Tree{ + + public $selector; + public $arguments; + public $index; + public $currentFileInfo; + + public $important; + public $type = 'MixinCall'; + + /** + * less.js: tree.mixin.Call + * + */ + public function __construct($elements, $args, $index, $currentFileInfo, $important = false){ + $this->selector = new Less_Tree_Selector($elements); + $this->arguments = $args; + $this->index = $index; + $this->currentFileInfo = $currentFileInfo; + $this->important = $important; + } + + //function accept($visitor){ + // $this->selector = $visitor->visit($this->selector); + // $this->arguments = $visitor->visit($this->arguments); + //} + + + public function compile($env){ + + $rules = array(); + $match = false; + $isOneFound = false; + $candidates = array(); + $defaultUsed = false; + $conditionResult = array(); + + $args = array(); + foreach($this->arguments as $a){ + $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) ); + } + + foreach($env->frames as $frame){ + + $mixins = $frame->find($this->selector); + + if( !$mixins ){ + continue; + } + + $isOneFound = true; + $defNone = 0; + $defTrue = 1; + $defFalse = 2; + + // To make `default()` function independent of definition order we have two "subpasses" here. + // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`), + // and build candidate list with corresponding flags. Then, when we know all possible matches, + // we make a final decision. + + $mixins_len = count($mixins); + for( $m = 0; $m < $mixins_len; $m++ ){ + $mixin = $mixins[$m]; + + if( $this->IsRecursive( $env, $mixin ) ){ + continue; + } + + if( $mixin->matchArgs($args, $env) ){ + + $candidate = array('mixin' => $mixin, 'group' => $defNone); + + if( $mixin instanceof Less_Tree_Ruleset ){ + + for( $f = 0; $f < 2; $f++ ){ + Less_Tree_DefaultFunc::value($f); + $conditionResult[$f] = $mixin->matchCondition( $args, $env); + } + if( $conditionResult[0] || $conditionResult[1] ){ + if( $conditionResult[0] != $conditionResult[1] ){ + $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse; + } + + $candidates[] = $candidate; + } + }else{ + $candidates[] = $candidate; + } + + $match = true; + } + } + + Less_Tree_DefaultFunc::reset(); + + + $count = array(0, 0, 0); + for( $m = 0; $m < count($candidates); $m++ ){ + $count[ $candidates[$m]['group'] ]++; + } + + if( $count[$defNone] > 0 ){ + $defaultResult = $defFalse; + } else { + $defaultResult = $defTrue; + if( ($count[$defTrue] + $count[$defFalse]) > 1 ){ + throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' ); + } + } + + + $candidates_length = count($candidates); + $length_1 = ($candidates_length == 1); + + for( $m = 0; $m < $candidates_length; $m++){ + $candidate = $candidates[$m]['group']; + if( ($candidate === $defNone) || ($candidate === $defaultResult) ){ + try{ + $mixin = $candidates[$m]['mixin']; + if( !($mixin instanceof Less_Tree_Mixin_Definition) ){ + $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false); + $mixin->originalRuleset = $mixins[$m]->originalRuleset; + } + $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules); + } catch (Exception $e) { + //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']); + throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo); + } + } + } + + if( $match ){ + if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){ + Less_Tree::ReferencedArray($rules); + } + + return $rules; + } + } + + if( $isOneFound ){ + throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo); + + }else{ + throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index); + } + + } + + /** + * Format the args for use in exception messages + * + */ + private function Format($args){ + $message = array(); + if( $args ){ + foreach($args as $a){ + $argValue = ''; + if( $a['name'] ){ + $argValue .= $a['name'] . ':'; + } + if( is_object($a['value']) ){ + $argValue .= $a['value']->toCSS(); + }else{ + $argValue .= '???'; + } + $message[] = $argValue; + } + } + return implode(', ',$message); + } + + + /** + * Are we in a recursive mixin call? + * + * @return bool + */ + private function IsRecursive( $env, $mixin ){ + + foreach($env->frames as $recur_frame){ + if( !($mixin instanceof Less_Tree_Mixin_Definition) ){ + + if( $mixin === $recur_frame ){ + return true; + } + + if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){ + return true; + } + } + } + + return false; + } + +} + + + + +class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{ + public $name; + public $selectors; + public $params; + public $arity = 0; + public $rules; + public $lookups = array(); + public $required = 0; + public $frames = array(); + public $condition; + public $variadic; + public $type = 'MixinDefinition'; + + + // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition + public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){ + $this->name = $name; + $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name)))); + + $this->params = $params; + $this->condition = $condition; + $this->variadic = $variadic; + $this->rules = $rules; + + if( $params ){ + $this->arity = count($params); + foreach( $params as $p ){ + if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) { + $this->required++; + } + } + } + + $this->frames = $frames; + $this->SetRulesetIndex(); + } + + + + //function accept( $visitor ){ + // $this->params = $visitor->visit($this->params); + // $this->rules = $visitor->visit($this->rules); + // $this->condition = $visitor->visit($this->condition); + //} + + + public function toCSS(){ + return ''; + } + + // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams + public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){ + $frame = new Less_Tree_Ruleset(null, array()); + $params = $this->params; + $mixinEnv = null; + $argsLength = 0; + + if( $args ){ + $argsLength = count($args); + for($i = 0; $i < $argsLength; $i++ ){ + $arg = $args[$i]; + + if( $arg && $arg['name'] ){ + $isNamedFound = false; + + foreach($params as $j => $param){ + if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) { + $evaldArguments[$j] = $arg['value']->compile($env); + array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) ); + $isNamedFound = true; + break; + } + } + if ($isNamedFound) { + array_splice($args, $i, 1); + $i--; + $argsLength--; + continue; + } else { + throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found'); + } + } + } + } + + $argIndex = 0; + foreach($params as $i => $param){ + + if ( isset($evaldArguments[$i]) ){ continue; } + + $arg = null; + if( isset($args[$argIndex]) ){ + $arg = $args[$argIndex]; + } + + if (isset($param['name']) && $param['name']) { + + if( isset($param['variadic']) ){ + $varargs = array(); + for ($j = $argIndex; $j < $argsLength; $j++) { + $varargs[] = $args[$j]['value']->compile($env); + } + $expression = new Less_Tree_Expression($varargs); + array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env))); + }else{ + $val = ($arg && $arg['value']) ? $arg['value'] : false; + + if ($val) { + $val = $val->compile($env); + } else if ( isset($param['value']) ) { + + if( !$mixinEnv ){ + $mixinEnv = new Less_Environment(); + $mixinEnv->frames = array_merge( array($frame), $mixinFrames); + } + + $val = $param['value']->compile($mixinEnv); + $frame->resetCache(); + } else { + throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")"); + } + + array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val)); + $evaldArguments[$i] = $val; + } + } + + if ( isset($param['variadic']) && $args) { + for ($j = $argIndex; $j < $argsLength; $j++) { + $evaldArguments[$j] = $args[$j]['value']->compile($env); + } + } + $argIndex++; + } + + ksort($evaldArguments); + $evaldArguments = array_values($evaldArguments); + + return $frame; + } + + public function compile($env) { + if( $this->frames ){ + return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames ); + } + return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames ); + } + + public function evalCall($env, $args = NULL, $important = NULL) { + + Less_Environment::$mixin_stack++; + + $_arguments = array(); + + if( $this->frames ){ + $mixinFrames = array_merge($this->frames, $env->frames); + }else{ + $mixinFrames = $env->frames; + } + + $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments); + + $ex = new Less_Tree_Expression($_arguments); + array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env))); + + + $ruleset = new Less_Tree_Ruleset(null, $this->rules); + $ruleset->originalRuleset = $this->ruleset_id; + + + $ruleSetEnv = new Less_Environment(); + $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames ); + $ruleset = $ruleset->compile( $ruleSetEnv ); + + if( $important ){ + $ruleset = $ruleset->makeImportant(); + } + + Less_Environment::$mixin_stack--; + + return $ruleset; + } + + + public function matchCondition($args, $env) { + + if( !$this->condition ){ + return true; + } + + // set array to prevent error on array_merge + if(!is_array($this->frames)) { + $this->frames = array(); + } + + $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args ); + + $compile_env = new Less_Environment(); + $compile_env->frames = array_merge( + array($frame) // the parameter variables + , $this->frames // the parent namespace/mixin frames + , $env->frames // the current environment frames + ); + + $compile_env->functions = $env->functions; + + return (bool)$this->condition->compile($compile_env); + } + + public function matchArgs($args, $env = NULL){ + $argsLength = count($args); + + if( !$this->variadic ){ + if( $argsLength < $this->required ){ + return false; + } + if( $argsLength > count($this->params) ){ + return false; + } + }else{ + if( $argsLength < ($this->required - 1)){ + return false; + } + } + + $len = min($argsLength, $this->arity); + + for( $i = 0; $i < $len; $i++ ){ + if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){ + if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){ + return false; + } + } + } + + return true; + } + +} + + +/** + * Extend Finder Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_Visitor_extendFinder extends Less_Visitor{ + + public $contexts = array(); + public $allExtendsStack; + public $foundExtends; + + public function __construct(){ + $this->contexts = array(); + $this->allExtendsStack = array(array()); + parent::__construct(); + } + + /** + * @param Less_Tree_Ruleset $root + */ + public function run($root){ + $root = $this->visitObj($root); + $root->allExtends =& $this->allExtendsStack[0]; + return $root; + } + + public function visitRule($ruleNode, &$visitDeeper ){ + $visitDeeper = false; + } + + public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ + $visitDeeper = false; + } + + public function visitRuleset($rulesetNode){ + + if( $rulesetNode->root ){ + return; + } + + $allSelectorsExtendList = array(); + + // get &:extend(.a); rules which apply to all selectors in this ruleset + if( $rulesetNode->rules ){ + foreach($rulesetNode->rules as $rule){ + if( $rule instanceof Less_Tree_Extend ){ + $allSelectorsExtendList[] = $rule; + $rulesetNode->extendOnEveryPath = true; + } + } + } + + + // now find every selector and apply the extends that apply to all extends + // and the ones which apply to an individual extend + foreach($rulesetNode->paths as $selectorPath){ + $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1]; + + $j = 0; + foreach($selector->extendList as $extend){ + $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j); + } + foreach($allSelectorsExtendList as $extend){ + $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j); + } + } + + $this->contexts[] = $rulesetNode->selectors; + } + + public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){ + $this->foundExtends = true; + $extend = clone $extend; + $extend->findSelfSelectors( $selectorPath ); + $extend->ruleset = $rulesetNode; + if( $j === 0 ){ + $extend->firstExtendOnThisSelectorPath = true; + } + + $end_key = count($this->allExtendsStack)-1; + $this->allExtendsStack[$end_key][] = $extend; + $j++; + } + + + public function visitRulesetOut( $rulesetNode ){ + if( !is_object($rulesetNode) || !$rulesetNode->root ){ + array_pop($this->contexts); + } + } + + public function visitMedia( $mediaNode ){ + $mediaNode->allExtends = array(); + $this->allExtendsStack[] =& $mediaNode->allExtends; + } + + public function visitMediaOut(){ + array_pop($this->allExtendsStack); + } + + public function visitDirective( $directiveNode ){ + $directiveNode->allExtends = array(); + $this->allExtendsStack[] =& $directiveNode->allExtends; + } + + public function visitDirectiveOut(){ + array_pop($this->allExtendsStack); + } +} + + + + +/* +class Less_Visitor_import extends Less_VisitorReplacing{ + + public $_visitor; + public $_importer; + public $importCount; + + function __construct( $evalEnv ){ + $this->env = $evalEnv; + $this->importCount = 0; + parent::__construct(); + } + + + function run( $root ){ + $root = $this->visitObj($root); + $this->isFinished = true; + + //if( $this->importCount === 0) { + // $this->_finish(); + //} + } + + function visitImport($importNode, &$visitDeeper ){ + $importVisitor = $this; + $inlineCSS = $importNode->options['inline']; + + if( !$importNode->css || $inlineCSS ){ + $evaldImportNode = $importNode->compileForImport($this->env); + + if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){ + $importNode = $evaldImportNode; + $this->importCount++; + $env = clone $this->env; + + if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){ + $env->importMultiple = true; + } + + //get path & uri + $path_and_uri = null; + if( is_callable(Less_Parser::$options['import_callback']) ){ + $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode); + } + + if( !$path_and_uri ){ + $path_and_uri = $importNode->PathAndUri(); + } + + if( $path_and_uri ){ + list($full_path, $uri) = $path_and_uri; + }else{ + $full_path = $uri = $importNode->getPath(); + } + + + //import once + if( $importNode->skip( $full_path, $env) ){ + return array(); + } + + if( $importNode->options['inline'] ){ + //todo needs to reference css file not import + //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true ); + + Less_Parser::AddParsedFile($full_path); + $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true ); + + if( $importNode->features ){ + return new Less_Tree_Media( array($contents), $importNode->features->value ); + } + + return array( $contents ); + } + + + // css ? + if( $importNode->css ){ + $features = ( $importNode->features ? $importNode->features->compile($env) : null ); + return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index); + } + + return $importNode->ParseImport( $full_path, $uri, $env ); + } + + } + + $visitDeeper = false; + return $importNode; + } + + + function visitRule( $ruleNode, &$visitDeeper ){ + $visitDeeper = false; + return $ruleNode; + } + + function visitDirective($directiveNode, $visitArgs){ + array_unshift($this->env->frames,$directiveNode); + return $directiveNode; + } + + function visitDirectiveOut($directiveNode) { + array_shift($this->env->frames); + } + + function visitMixinDefinition($mixinDefinitionNode, $visitArgs) { + array_unshift($this->env->frames,$mixinDefinitionNode); + return $mixinDefinitionNode; + } + + function visitMixinDefinitionOut($mixinDefinitionNode) { + array_shift($this->env->frames); + } + + function visitRuleset($rulesetNode, $visitArgs) { + array_unshift($this->env->frames,$rulesetNode); + return $rulesetNode; + } + + function visitRulesetOut($rulesetNode) { + array_shift($this->env->frames); + } + + function visitMedia($mediaNode, $visitArgs) { + array_unshift($this->env->frames, $mediaNode->ruleset); + return $mediaNode; + } + + function visitMediaOut($mediaNode) { + array_shift($this->env->frames); + } + +} +*/ + + + + +/** + * Join Selector Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_Visitor_joinSelector extends Less_Visitor{ + + public $contexts = array( array() ); + + /** + * @param Less_Tree_Ruleset $root + */ + public function run( $root ){ + return $this->visitObj($root); + } + + public function visitRule( $ruleNode, &$visitDeeper ){ + $visitDeeper = false; + } + + public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ + $visitDeeper = false; + } + + public function visitRuleset( $rulesetNode ){ + + $paths = array(); + + if( !$rulesetNode->root ){ + $selectors = array(); + + if( $rulesetNode->selectors && $rulesetNode->selectors ){ + foreach($rulesetNode->selectors as $selector){ + if( $selector->getIsOutput() ){ + $selectors[] = $selector; + } + } + } + + if( !$selectors ){ + $rulesetNode->selectors = null; + $rulesetNode->rules = null; + }else{ + $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1]; + $paths = $rulesetNode->joinSelectors( $context, $selectors); + } + + $rulesetNode->paths = $paths; + } + + $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths + } + + public function visitRulesetOut(){ + array_pop($this->contexts); + } + + public function visitMedia($mediaNode) { + $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1]; + + if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){ + $mediaNode->rules[0]->root = true; + } + } + +} + + + +/** + * Process Extends Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_Visitor_processExtends extends Less_Visitor{ + + public $allExtendsStack; + + /** + * @param Less_Tree_Ruleset $root + */ + public function run( $root ){ + $extendFinder = new Less_Visitor_extendFinder(); + $extendFinder->run( $root ); + if( !$extendFinder->foundExtends){ + return $root; + } + + $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends); + + $this->allExtendsStack = array(); + $this->allExtendsStack[] = &$root->allExtends; + + return $this->visitObj( $root ); + } + + private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){ + // + // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting + // the selector we would do normally, but we are also adding an extend with the same target selector + // this means this new extend can then go and alter other extends + // + // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors + // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if + // we look at each selector at a time, as is done in visitRuleset + + $extendsToAdd = array(); + + + //loop through comparing every extend with every target extend. + // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place + // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one + // and the second is the target. + // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the + // case when processing media queries + for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){ + for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){ + + $extend = $extendsList[$extendIndex]; + $targetExtend = $extendsListTarget[$targetExtendIndex]; + + // look for circular references + if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){ + continue; + } + + // find a match in the target extends self selector (the bit before :extend) + $selectorPath = array( $targetExtend->selfSelectors[0] ); + $matches = $this->findMatch( $extend, $selectorPath); + + + if( $matches ){ + + // we found a match, so for each self selector.. + foreach($extend->selfSelectors as $selfSelector ){ + + + // process the extend as usual + $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector); + + // but now we create a new extend from it + $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0); + $newExtend->selfSelectors = $newSelector; + + // add the extend onto the list of extends for that selector + end($newSelector)->extendList = array($newExtend); + //$newSelector[ count($newSelector)-1]->extendList = array($newExtend); + + // record that we need to add it. + $extendsToAdd[] = $newExtend; + $newExtend->ruleset = $targetExtend->ruleset; + + //remember its parents for circular references + $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids); + + // only process the selector once.. if we have :extend(.a,.b) then multiple + // extends will look at the same selector path, so when extending + // we know that any others will be duplicates in terms of what is added to the css + if( $targetExtend->firstExtendOnThisSelectorPath ){ + $newExtend->firstExtendOnThisSelectorPath = true; + $targetExtend->ruleset->paths[] = $newSelector; + } + } + } + } + } + + if( $extendsToAdd ){ + // try to detect circular references to stop a stack overflow. + // may no longer be needed. $this->extendChainCount++; + if( $iterationCount > 100) { + + try{ + $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS(); + $selectorTwo = $extendsToAdd[0]->selector->toCSS(); + }catch(Exception $e){ + $selectorOne = "{unable to calculate}"; + $selectorTwo = "{unable to calculate}"; + } + + throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")"); + } + + // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e... + $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1); + } + + return array_merge($extendsList, $extendsToAdd); + } + + + protected function visitRule( $ruleNode, &$visitDeeper ){ + $visitDeeper = false; + } + + protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ + $visitDeeper = false; + } + + protected function visitSelector( $selectorNode, &$visitDeeper ){ + $visitDeeper = false; + } + + protected function visitRuleset($rulesetNode){ + + + if( $rulesetNode->root ){ + return; + } + + $allExtends = end($this->allExtendsStack); + $paths_len = count($rulesetNode->paths); + + // look at each selector path in the ruleset, find any extend matches and then copy, find and replace + foreach($allExtends as $allExtend){ + for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){ + + // extending extends happens initially, before the main pass + if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){ + continue; + } + + $selectorPath = $rulesetNode->paths[$pathIndex]; + + if( end($selectorPath)->extendList ){ + continue; + } + + $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath); + + } + } + } + + + private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){ + $matches = $this->findMatch($extend, $selectorPath); + + if( $matches ){ + foreach($extend->selfSelectors as $selfSelector ){ + $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector); + } + } + } + + + + private function findMatch($extend, $haystackSelectorPath ){ + + + if( !$this->HasMatches($extend, $haystackSelectorPath) ){ + return false; + } + + + // + // look through the haystack selector path to try and find the needle - extend.selector + // returns an array of selector matches that can then be replaced + // + $needleElements = $extend->selector->elements; + $potentialMatches = array(); + $potentialMatches_len = 0; + $potentialMatch = null; + $matches = array(); + + + + // loop through the haystack elements + $haystack_path_len = count($haystackSelectorPath); + for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){ + $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex]; + + $haystack_elements_len = count($hackstackSelector->elements); + for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){ + + $haystackElement = $hackstackSelector->elements[$hackstackElementIndex]; + + // if we allow elements before our match we can add a potential match every time. otherwise only at the first element. + if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){ + $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator); + $potentialMatches_len++; + } + + for($i = 0; $i < $potentialMatches_len; $i++ ){ + + $potentialMatch = &$potentialMatches[$i]; + $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ); + + + // if we are still valid and have finished, test whether we have elements after and whether these are allowed + if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){ + $potentialMatch['finished'] = true; + + if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){ + $potentialMatch = null; + } + } + + // if null we remove, if not, we are still valid, so either push as a valid match or continue + if( $potentialMatch ){ + if( $potentialMatch['finished'] ){ + $potentialMatch['length'] = $extend->selector->elements_len; + $potentialMatch['endPathIndex'] = $haystackSelectorIndex; + $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match + $potentialMatches = array(); // we don't allow matches to overlap, so start matching again + $potentialMatches_len = 0; + $matches[] = $potentialMatch; + } + continue; + } + + array_splice($potentialMatches, $i, 1); + $potentialMatches_len--; + $i--; + } + } + } + + return $matches; + } + + + // Before going through all the nested loops, lets check to see if a match is possible + // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s + private function HasMatches($extend, $haystackSelectorPath){ + + if( !$extend->selector->cacheable ){ + return true; + } + + $first_el = $extend->selector->_oelements[0]; + + foreach($haystackSelectorPath as $hackstackSelector){ + if( !$hackstackSelector->cacheable ){ + return true; + } + + if( in_array($first_el, $hackstackSelector->_oelements) ){ + return true; + } + } + + return false; + } + + + /** + * @param integer $hackstackElementIndex + */ + private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){ + + + if( $potentialMatch['matched'] > 0 ){ + + // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't + // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out + // what the resulting combinator will be + $targetCombinator = $haystackElement->combinator; + if( $targetCombinator === '' && $hackstackElementIndex === 0 ){ + $targetCombinator = ' '; + } + + if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){ + return null; + } + } + + // if we don't match, null our match to indicate failure + if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){ + return null; + } + + $potentialMatch['finished'] = false; + $potentialMatch['matched']++; + + return $potentialMatch; + } + + + private function isElementValuesEqual( $elementValue1, $elementValue2 ){ + + if( $elementValue1 === $elementValue2 ){ + return true; + } + + if( is_string($elementValue1) || is_string($elementValue2) ) { + return false; + } + + if( $elementValue1 instanceof Less_Tree_Attribute ){ + return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 ); + } + + $elementValue1 = $elementValue1->value; + if( $elementValue1 instanceof Less_Tree_Selector ){ + return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 ); + } + + return false; + } + + + /** + * @param Less_Tree_Selector $elementValue1 + */ + private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){ + + $elementValue2 = $elementValue2->value; + if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){ + return false; + } + + for( $i = 0; $i < $elementValue1->elements_len; $i++ ){ + + if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){ + if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){ + return false; + } + } + + if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){ + return false; + } + } + + return true; + } + + + /** + * @param Less_Tree_Attribute $elementValue1 + */ + private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){ + + if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){ + return false; + } + + if( !$elementValue1->value || !$elementValue2->value ){ + if( $elementValue1->value || $elementValue2->value ) { + return false; + } + return true; + } + + $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value ); + $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value ); + + return $elementValue1 === $elementValue2; + } + + + private function extendSelector($matches, $selectorPath, $replacementSelector){ + + //for a set of matches, replace each match with the replacement selector + + $currentSelectorPathIndex = 0; + $currentSelectorPathElementIndex = 0; + $path = array(); + $selectorPath_len = count($selectorPath); + + for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){ + + + $match = $matches[$matchIndex]; + $selector = $selectorPath[ $match['pathIndex'] ]; + + $firstElement = new Less_Tree_Element( + $match['initialCombinator'], + $replacementSelector->elements[0]->value, + $replacementSelector->elements[0]->index, + $replacementSelector->elements[0]->currentFileInfo + ); + + if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){ + $last_path = end($path); + $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex)); + $currentSelectorPathElementIndex = 0; + $currentSelectorPathIndex++; + } + + $newElements = array_merge( + array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice + , array($firstElement) + , array_slice($replacementSelector->elements,1) + ); + + if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){ + $last_key = count($path)-1; + $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements); + }else{ + $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] )); + $path[] = new Less_Tree_Selector( $newElements ); + } + + $currentSelectorPathIndex = $match['endPathIndex']; + $currentSelectorPathElementIndex = $match['endPathElementIndex']; + if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){ + $currentSelectorPathElementIndex = 0; + $currentSelectorPathIndex++; + } + } + + if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){ + $last_path = end($path); + $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex)); + $currentSelectorPathIndex++; + } + + $slice_len = $selectorPath_len - $currentSelectorPathIndex; + $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len)); + + return $path; + } + + + protected function visitMedia( $mediaNode ){ + $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) ); + $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends); + } + + protected function visitMediaOut(){ + array_pop( $this->allExtendsStack ); + } + + protected function visitDirective( $directiveNode ){ + $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) ); + $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends); + } + + protected function visitDirectiveOut(){ + array_pop($this->allExtendsStack); + } + +} + +/** + * toCSS Visitor + * + * @package Less + * @subpackage visitor + */ +class Less_Visitor_toCSS extends Less_VisitorReplacing{ + + private $charset; + + public function __construct(){ + parent::__construct(); + } + + /** + * @param Less_Tree_Ruleset $root + */ + public function run( $root ){ + return $this->visitObj($root); + } + + public function visitRule( $ruleNode ){ + if( $ruleNode->variable ){ + return array(); + } + return $ruleNode; + } + + public function visitMixinDefinition($mixinNode){ + // mixin definitions do not get eval'd - this means they keep state + // so we have to clear that state here so it isn't used if toCSS is called twice + $mixinNode->frames = array(); + return array(); + } + + public function visitExtend(){ + return array(); + } + + public function visitComment( $commentNode ){ + if( $commentNode->isSilent() ){ + return array(); + } + return $commentNode; + } + + public function visitMedia( $mediaNode, &$visitDeeper ){ + $mediaNode->accept($this); + $visitDeeper = false; + + if( !$mediaNode->rules ){ + return array(); + } + return $mediaNode; + } + + public function visitDirective( $directiveNode ){ + if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){ + return array(); + } + if( $directiveNode->name === '@charset' ){ + // Only output the debug info together with subsequent @charset definitions + // a comment (or @media statement) before the actual @charset directive would + // be considered illegal css as it has to be on the first line + if( isset($this->charset) && $this->charset ){ + + //if( $directiveNode->debugInfo ){ + // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n"); + // $comment->debugInfo = $directiveNode->debugInfo; + // return $this->visit($comment); + //} + + + return array(); + } + $this->charset = true; + } + return $directiveNode; + } + + public function checkPropertiesInRoot( $rulesetNode ){ + + if( !$rulesetNode->firstRoot ){ + return; + } + + foreach($rulesetNode->rules as $ruleNode){ + if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){ + $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null); + throw new Less_Exception_Compiler($msg); + } + } + } + + + public function visitRuleset( $rulesetNode, &$visitDeeper ){ + + $visitDeeper = false; + + $this->checkPropertiesInRoot( $rulesetNode ); + + if( $rulesetNode->root ){ + return $this->visitRulesetRoot( $rulesetNode ); + } + + $rulesets = array(); + $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode); + + + // Compile rules and rulesets + $nodeRuleCnt = count($rulesetNode->rules); + for( $i = 0; $i < $nodeRuleCnt; ){ + $rule = $rulesetNode->rules[$i]; + + if( property_exists($rule,'rules') ){ + // visit because we are moving them out from being a child + $rulesets[] = $this->visitObj($rule); + array_splice($rulesetNode->rules,$i,1); + $nodeRuleCnt--; + continue; + } + $i++; + } + + + // accept the visitor to remove rules and refactor itself + // then we can decide now whether we want it or not + if( $nodeRuleCnt > 0 ){ + $rulesetNode->accept($this); + + if( $rulesetNode->rules ){ + + if( count($rulesetNode->rules) > 1 ){ + $this->_mergeRules( $rulesetNode->rules ); + $this->_removeDuplicateRules( $rulesetNode->rules ); + } + + // now decide whether we keep the ruleset + if( $rulesetNode->paths ){ + //array_unshift($rulesets, $rulesetNode); + array_splice($rulesets,0,0,array($rulesetNode)); + } + } + + } + + + if( count($rulesets) === 1 ){ + return $rulesets[0]; + } + return $rulesets; + } + + + /** + * Helper function for visitiRuleset + * + * return array|Less_Tree_Ruleset + */ + private function visitRulesetRoot( $rulesetNode ){ + $rulesetNode->accept( $this ); + if( $rulesetNode->firstRoot || $rulesetNode->rules ){ + return $rulesetNode; + } + return array(); + } + + + /** + * Helper function for visitRuleset() + * + * @return array + */ + private function visitRulesetPaths($rulesetNode){ + + $paths = array(); + foreach($rulesetNode->paths as $p){ + if( $p[0]->elements[0]->combinator === ' ' ){ + $p[0]->elements[0]->combinator = ''; + } + + foreach($p as $pi){ + if( $pi->getIsReferenced() && $pi->getIsOutput() ){ + $paths[] = $p; + break; + } + } + } + + return $paths; + } + + protected function _removeDuplicateRules( &$rules ){ + // remove duplicates + $ruleCache = array(); + for( $i = count($rules)-1; $i >= 0 ; $i-- ){ + $rule = $rules[$i]; + if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){ + + if( !isset($ruleCache[$rule->name]) ){ + $ruleCache[$rule->name] = $rule; + }else{ + $ruleList =& $ruleCache[$rule->name]; + + if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){ + $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() ); + } + + $ruleCSS = $rule->toCSS(); + if( array_search($ruleCSS,$ruleList) !== false ){ + array_splice($rules,$i,1); + }else{ + $ruleList[] = $ruleCSS; + } + } + } + } + } + + protected function _mergeRules( &$rules ){ + $groups = array(); + + //obj($rules); + + $rules_len = count($rules); + for( $i = 0; $i < $rules_len; $i++ ){ + $rule = $rules[$i]; + + if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){ + + $key = $rule->name; + if( $rule->important ){ + $key .= ',!'; + } + + if( !isset($groups[$key]) ){ + $groups[$key] = array(); + }else{ + array_splice($rules, $i--, 1); + $rules_len--; + } + + $groups[$key][] = $rule; + } + } + + + foreach($groups as $parts){ + + if( count($parts) > 1 ){ + $rule = $parts[0]; + $spacedGroups = array(); + $lastSpacedGroup = array(); + $parts_mapped = array(); + foreach($parts as $p){ + if( $p->merge === '+' ){ + if( $lastSpacedGroup ){ + $spacedGroups[] = self::toExpression($lastSpacedGroup); + } + $lastSpacedGroup = array(); + } + $lastSpacedGroup[] = $p; + } + + $spacedGroups[] = self::toExpression($lastSpacedGroup); + $rule->value = self::toValue($spacedGroups); + } + } + + } + + public static function toExpression($values){ + $mapped = array(); + foreach($values as $p){ + $mapped[] = $p->value; + } + return new Less_Tree_Expression( $mapped ); + } + + public static function toValue($values){ + //return new Less_Tree_Value($values); ?? + + $mapped = array(); + foreach($values as $p){ + $mapped[] = $p; + } + return new Less_Tree_Value($mapped); + } +} + + + +/** + * Parser Exception + * + * @package Less + * @subpackage exception + */ +class Less_Exception_Parser extends Exception{ + + /** + * The current file + * + * @var Less_ImportedFile + */ + public $currentFile; + + /** + * The current parser index + * + * @var integer + */ + public $index; + + protected $input; + + protected $details = array(); + + + /** + * Constructor + * + * @param string $message + * @param Exception $previous Previous exception + * @param integer $index The current parser index + * @param Less_FileInfo|string $currentFile The file + * @param integer $code The exception code + */ + public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){ + + if (PHP_VERSION_ID < 50300) { + $this->previous = $previous; + parent::__construct($message, $code); + } else { + parent::__construct($message, $code, $previous); + } + + $this->currentFile = $currentFile; + $this->index = $index; + + $this->genMessage(); + } + + + protected function getInput(){ + + if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){ + $this->input = file_get_contents( $this->currentFile['filename'] ); + } + } + + + + /** + * Converts the exception to string + * + * @return string + */ + public function genMessage(){ + + if( $this->currentFile && $this->currentFile['filename'] ){ + $this->message .= ' in '.basename($this->currentFile['filename']); + } + + if( $this->index !== null ){ + $this->getInput(); + if( $this->input ){ + $line = self::getLineNumber(); + $this->message .= ' on line '.$line.', column '.self::getColumn(); + + $lines = explode("\n",$this->input); + + $count = count($lines); + $start_line = max(0, $line-3); + $last_line = min($count, $start_line+6); + $num_len = strlen($last_line); + for( $i = $start_line; $i < $last_line; $i++ ){ + $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i]; + } + } + } + + } + + /** + * Returns the line number the error was encountered + * + * @return integer + */ + public function getLineNumber(){ + if( $this->index ){ + // https://bugs.php.net/bug.php?id=49790 + if (ini_get("mbstring.func_overload")) { + return substr_count(substr($this->input, 0, $this->index), "\n") + 1; + } else { + return substr_count($this->input, "\n", 0, $this->index) + 1; + } + } + return 1; + } + + + /** + * Returns the column the error was encountered + * + * @return integer + */ + public function getColumn(){ + + $part = substr($this->input, 0, $this->index); + $pos = strrpos($part,"\n"); + return $this->index - $pos; + } + +} + + +/** + * Chunk Exception + * + * @package Less + * @subpackage exception + */ +class Less_Exception_Chunk extends Less_Exception_Parser{ + + + protected $parserCurrentIndex = 0; + + protected $emitFrom = 0; + + protected $input_len; + + + /** + * Constructor + * + * @param string $input + * @param Exception $previous Previous exception + * @param integer $index The current parser index + * @param Less_FileInfo|string $currentFile The file + * @param integer $code The exception code + */ + public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){ + + $this->message = 'ParseError: Unexpected input'; //default message + + $this->index = $index; + + $this->currentFile = $currentFile; + + $this->input = $input; + $this->input_len = strlen($input); + + $this->Chunks(); + $this->genMessage(); + } + + + /** + * See less.js chunks() + * We don't actually need the chunks + * + */ + protected function Chunks(){ + $level = 0; + $parenLevel = 0; + $lastMultiCommentEndBrace = null; + $lastOpening = null; + $lastMultiComment = null; + $lastParen = null; + + for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){ + $cc = $this->CharCode($this->parserCurrentIndex); + if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) { + // a-z or whitespace + continue; + } + + switch ($cc) { + + // ( + case 40: + $parenLevel++; + $lastParen = $this->parserCurrentIndex; + continue; + + // ) + case 41: + $parenLevel--; + if( $parenLevel < 0 ){ + return $this->fail("missing opening `(`"); + } + continue; + + // ; + case 59: + //if (!$parenLevel) { $this->emitChunk(); } + continue; + + // { + case 123: + $level++; + $lastOpening = $this->parserCurrentIndex; + continue; + + // } + case 125: + $level--; + if( $level < 0 ){ + return $this->fail("missing opening `{`"); + + } + //if (!$level && !$parenLevel) { $this->emitChunk(); } + continue; + // \ + case 92: + if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; } + return $this->fail("unescaped `\\`"); + + // ", ' and ` + case 34: + case 39: + case 96: + $matched = 0; + $currentChunkStartIndex = $this->parserCurrentIndex; + for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) { + $cc2 = $this->CharCode($this->parserCurrentIndex); + if ($cc2 > 96) { continue; } + if ($cc2 == $cc) { $matched = 1; break; } + if ($cc2 == 92) { // \ + if ($this->parserCurrentIndex == $this->input_len - 1) { + return $this->fail("unescaped `\\`"); + } + $this->parserCurrentIndex++; + } + } + if ($matched) { continue; } + return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex); + + // /, check for comment + case 47: + if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; } + $cc2 = $this->CharCode($this->parserCurrentIndex+1); + if ($cc2 == 47) { + // //, find lnfeed + for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) { + $cc2 = $this->CharCode($this->parserCurrentIndex); + if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; } + } + } else if ($cc2 == 42) { + // /*, find */ + $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex; + for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) { + $cc2 = $this->CharCode($this->parserCurrentIndex); + if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; } + if ($cc2 != 42) { continue; } + if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; } + } + if ($this->parserCurrentIndex == $this->input_len - 1) { + return $this->fail("missing closing `*/`", $currentChunkStartIndex); + } + } + continue; + + // *, check for unmatched */ + case 42: + if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) { + return $this->fail("unmatched `/*`"); + } + continue; + } + } + + if( $level !== 0 ){ + if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){ + return $this->fail("missing closing `}` or `*/`", $lastOpening); + } else { + return $this->fail("missing closing `}`", $lastOpening); + } + } else if ( $parenLevel !== 0 ){ + return $this->fail("missing closing `)`", $lastParen); + } + + + //chunk didn't fail + + + //$this->emitChunk(true); + } + + public function CharCode($pos){ + return ord($this->input[$pos]); + } + + + public function fail( $msg, $index = null ){ + + if( !$index ){ + $this->index = $this->parserCurrentIndex; + }else{ + $this->index = $index; + } + $this->message = 'ParseError: '.$msg; + } + + + /* + function emitChunk( $force = false ){ + $len = $this->parserCurrentIndex - $this->emitFrom; + if ((($len < 512) && !$force) || !$len) { + return; + } + $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom ); + $this->emitFrom = $this->parserCurrentIndex + 1; + } + */ + +} + + +/** + * Compiler Exception + * + * @package Less + * @subpackage exception + */ +class Less_Exception_Compiler extends Less_Exception_Parser{ + +} + +/** + * Parser output with source map + * + * @package Less + * @subpackage Output + */ +class Less_Output_Mapped extends Less_Output { + + /** + * The source map generator + * + * @var Less_SourceMap_Generator + */ + protected $generator; + + /** + * Current line + * + * @var integer + */ + protected $lineNumber = 0; + + /** + * Current column + * + * @var integer + */ + protected $column = 0; + + /** + * Array of contents map (file and its content) + * + * @var array + */ + protected $contentsMap = array(); + + /** + * Constructor + * + * @param array $contentsMap Array of filename to contents map + * @param Less_SourceMap_Generator $generator + */ + public function __construct(array $contentsMap, $generator){ + $this->contentsMap = $contentsMap; + $this->generator = $generator; + } + + /** + * Adds a chunk to the stack + * The $index for less.php may be different from less.js since less.php does not chunkify inputs + * + * @param string $chunk + * @param string $fileInfo + * @param integer $index + * @param mixed $mapLines + */ + public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){ + + //ignore adding empty strings + if( $chunk === '' ){ + return; + } + + + $sourceLines = array(); + $sourceColumns = ' '; + + + if( $fileInfo ){ + + $url = $fileInfo['currentUri']; + + if( isset($this->contentsMap[$url]) ){ + $inputSource = substr($this->contentsMap[$url], 0, $index); + $sourceLines = explode("\n", $inputSource); + $sourceColumns = end($sourceLines); + }else{ + throw new Exception('Filename '.$url.' not in contentsMap'); + } + + } + + $lines = explode("\n", $chunk); + $columns = end($lines); + + if($fileInfo){ + + if(!$mapLines){ + $this->generator->addMapping( + $this->lineNumber + 1, // generated_line + $this->column, // generated_column + count($sourceLines), // original_line + strlen($sourceColumns), // original_column + $fileInfo + ); + }else{ + for($i = 0, $count = count($lines); $i < $count; $i++){ + $this->generator->addMapping( + $this->lineNumber + $i + 1, // generated_line + $i === 0 ? $this->column : 0, // generated_column + count($sourceLines) + $i, // original_line + $i === 0 ? strlen($sourceColumns) : 0, // original_column + $fileInfo + ); + } + } + } + + if(count($lines) === 1){ + $this->column += strlen($columns); + }else{ + $this->lineNumber += count($lines) - 1; + $this->column = strlen($columns); + } + + // add only chunk + parent::add($chunk); + } + +} + +/** + * Encode / Decode Base64 VLQ. + * + * @package Less + * @subpackage SourceMap + */ +class Less_SourceMap_Base64VLQ { + + /** + * Shift + * + * @var integer + */ + private $shift = 5; + + /** + * Mask + * + * @var integer + */ + private $mask = 0x1F; // == (1 << shift) == 0b00011111 + + /** + * Continuation bit + * + * @var integer + */ + private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 + + /** + * Char to integer map + * + * @var array + */ + private $charToIntMap = array( + 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, + 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, + 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, + 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, + 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, + 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, + 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, + 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, + 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, + ); + + /** + * Integer to char map + * + * @var array + */ + private $intToCharMap = array( + 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', + 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', + 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', + 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', + 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', + 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', + 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', + 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', + 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', + 63 => '/', + ); + + /** + * Constructor + */ + public function __construct(){ + // I leave it here for future reference + // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) + // { + // $this->charToIntMap[$char] = $i; + // $this->intToCharMap[$i] = $char; + // } + } + + /** + * Convert from a two-complement value to a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) + * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) + * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, + * even on a 64 bit machine. + * @param string $aValue + */ + public function toVLQSigned($aValue){ + return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); + } + + /** + * Convert to a two-complement value from a value where the sign bit is + * is placed in the least significant bit. For example, as decimals: + * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 + * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 + * We assume that the value was generated with a 32 bit machine in mind. + * Hence + * 1 becomes -2147483648 + * even on a 64 bit machine. + * @param integer $aValue + */ + public function fromVLQSigned($aValue){ + return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); + } + + /** + * Return the base 64 VLQ encoded value. + * + * @param string $aValue The value to encode + * @return string The encoded value + */ + public function encode($aValue){ + $encoded = ''; + $vlq = $this->toVLQSigned($aValue); + do + { + $digit = $vlq & $this->mask; + $vlq = $this->zeroFill($vlq, $this->shift); + if($vlq > 0){ + $digit |= $this->continuationBit; + } + $encoded .= $this->base64Encode($digit); + } while($vlq > 0); + + return $encoded; + } + + /** + * Return the value decoded from base 64 VLQ. + * + * @param string $encoded The encoded value to decode + * @return integer The decoded value + */ + public function decode($encoded){ + $vlq = 0; + $i = 0; + do + { + $digit = $this->base64Decode($encoded[$i]); + $vlq |= ($digit & $this->mask) << ($i * $this->shift); + $i++; + } while($digit & $this->continuationBit); + + return $this->fromVLQSigned($vlq); + } + + /** + * Right shift with zero fill. + * + * @param integer $a number to shift + * @param integer $b number of bits to shift + * @return integer + */ + public function zeroFill($a, $b){ + return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); + } + + /** + * Encode single 6-bit digit as base64. + * + * @param integer $number + * @return string + * @throws Exception If the number is invalid + */ + public function base64Encode($number){ + if($number < 0 || $number > 63){ + throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); + } + return $this->intToCharMap[$number]; + } + + /** + * Decode single 6-bit digit from base64 + * + * @param string $char + * @return number + * @throws Exception If the number is invalid + */ + public function base64Decode($char){ + if(!array_key_exists($char, $this->charToIntMap)){ + throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); + } + return $this->charToIntMap[$char]; + } + +} + + +/** + * Source map generator + * + * @package Less + * @subpackage Output + */ +class Less_SourceMap_Generator extends Less_Configurable { + + /** + * What version of source map does the generator generate? + */ + const VERSION = 3; + + /** + * Array of default options + * + * @var array + */ + protected $defaultOptions = array( + // an optional source root, useful for relocating source files + // on a server or removing repeated values in the 'sources' entry. + // This value is prepended to the individual entries in the 'source' field. + 'sourceRoot' => '', + + // an optional name of the generated code that this source map is associated with. + 'sourceMapFilename' => null, + + // url of the map + 'sourceMapURL' => null, + + // absolute path to a file to write the map to + 'sourceMapWriteTo' => null, + + // output source contents? + 'outputSourceFiles' => false, + + // base path for filename normalization + 'sourceMapRootpath' => '', + + // base path for filename normalization + 'sourceMapBasepath' => '' + ); + + /** + * The base64 VLQ encoder + * + * @var Less_SourceMap_Base64VLQ + */ + protected $encoder; + + /** + * Array of mappings + * + * @var array + */ + protected $mappings = array(); + + /** + * The root node + * + * @var Less_Tree_Ruleset + */ + protected $root; + + /** + * Array of contents map + * + * @var array + */ + protected $contentsMap = array(); + + /** + * File to content map + * + * @var array + */ + protected $sources = array(); + protected $source_keys = array(); + + /** + * Constructor + * + * @param Less_Tree_Ruleset $root The root node + * @param array $options Array of options + */ + public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){ + $this->root = $root; + $this->contentsMap = $contentsMap; + $this->encoder = new Less_SourceMap_Base64VLQ(); + + $this->SetOptions($options); + + $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true); + $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true); + } + + /** + * Generates the CSS + * + * @return string + */ + public function generateCSS(){ + $output = new Less_Output_Mapped($this->contentsMap, $this); + + // catch the output + $this->root->genCSS($output); + + + $sourceMapUrl = $this->getOption('sourceMapURL'); + $sourceMapFilename = $this->getOption('sourceMapFilename'); + $sourceMapContent = $this->generateJson(); + $sourceMapWriteTo = $this->getOption('sourceMapWriteTo'); + + if( !$sourceMapUrl && $sourceMapFilename ){ + $sourceMapUrl = $this->normalizeFilename($sourceMapFilename); + } + + // write map to a file + if( $sourceMapWriteTo ){ + $this->saveMap($sourceMapWriteTo, $sourceMapContent); + } + + // inline the map + if( !$sourceMapUrl ){ + $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent)); + } + + if( $sourceMapUrl ){ + $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) ); + } + + return $output->toString(); + } + + /** + * Saves the source map to a file + * + * @param string $file The absolute path to a file + * @param string $content The content to write + * @throws Exception If the file could not be saved + */ + protected function saveMap($file, $content){ + $dir = dirname($file); + // directory does not exist + if( !is_dir($dir) ){ + // FIXME: create the dir automatically? + throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); + } + // FIXME: proper saving, with dir write check! + if(file_put_contents($file, $content) === false){ + throw new Exception(sprintf('Cannot save the source map to "%s"', $file)); + } + return true; + } + + /** + * Normalizes the filename + * + * @param string $filename + * @return string + */ + protected function normalizeFilename($filename){ + + $filename = $this->fixWindowsPath($filename); + + $rootpath = $this->getOption('sourceMapRootpath'); + $basePath = $this->getOption('sourceMapBasepath'); + + // "Trim" the 'sourceMapBasepath' from the output filename. + if (strpos($filename, $basePath) === 0) { + $filename = substr($filename, strlen($basePath)); + } + + // Remove extra leading path separators. + if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){ + $filename = substr($filename, 1); + } + + return $rootpath . $filename; + } + + /** + * Adds a mapping + * + * @param integer $generatedLine The line number in generated file + * @param integer $generatedColumn The column number in generated file + * @param integer $originalLine The line number in original file + * @param integer $originalColumn The column number in original file + * @param string $sourceFile The original source file + */ + public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){ + + $this->mappings[] = array( + 'generated_line' => $generatedLine, + 'generated_column' => $generatedColumn, + 'original_line' => $originalLine, + 'original_column' => $originalColumn, + 'source_file' => $fileInfo['currentUri'] + ); + + $this->sources[$fileInfo['currentUri']] = $fileInfo['filename']; + } + + + /** + * Generates the JSON source map + * + * @return string + * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# + */ + protected function generateJson(){ + + $sourceMap = array(); + $mappings = $this->generateMappings(); + + // File version (always the first entry in the object) and must be a positive integer. + $sourceMap['version'] = self::VERSION; + + + // An optional name of the generated code that this source map is associated with. + $file = $this->getOption('sourceMapFilename'); + if( $file ){ + $sourceMap['file'] = $file; + } + + + // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. + $root = $this->getOption('sourceRoot'); + if( $root ){ + $sourceMap['sourceRoot'] = $root; + } + + + // A list of original sources used by the 'mappings' entry. + $sourceMap['sources'] = array(); + foreach($this->sources as $source_uri => $source_filename){ + $sourceMap['sources'][] = $this->normalizeFilename($source_filename); + } + + + // A list of symbol names used by the 'mappings' entry. + $sourceMap['names'] = array(); + + // A string with the encoded mapping data. + $sourceMap['mappings'] = $mappings; + + if( $this->getOption('outputSourceFiles') ){ + // An optional list of source content, useful when the 'source' can't be hosted. + // The contents are listed in the same order as the sources above. + // 'null' may be used if some original sources should be retrieved by name. + $sourceMap['sourcesContent'] = $this->getSourcesContent(); + } + + // less.js compat fixes + if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){ + unset($sourceMap['sourceRoot']); + } + + return json_encode($sourceMap); + } + + /** + * Returns the sources contents + * + * @return array|null + */ + protected function getSourcesContent(){ + if(empty($this->sources)){ + return; + } + $content = array(); + foreach($this->sources as $sourceFile){ + $content[] = file_get_contents($sourceFile); + } + return $content; + } + + /** + * Generates the mappings string + * + * @return string + */ + public function generateMappings(){ + + if( !count($this->mappings) ){ + return ''; + } + + $this->source_keys = array_flip(array_keys($this->sources)); + + + // group mappings by generated line number. + $groupedMap = $groupedMapEncoded = array(); + foreach($this->mappings as $m){ + $groupedMap[$m['generated_line']][] = $m; + } + ksort($groupedMap); + + $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; + + foreach($groupedMap as $lineNumber => $line_map){ + while(++$lastGeneratedLine < $lineNumber){ + $groupedMapEncoded[] = ';'; + } + + $lineMapEncoded = array(); + $lastGeneratedColumn = 0; + + foreach($line_map as $m){ + $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); + $lastGeneratedColumn = $m['generated_column']; + + // find the index + if( $m['source_file'] ){ + $index = $this->findFileIndex($m['source_file']); + if( $index !== false ){ + $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); + $lastOriginalIndex = $index; + + // lines are stored 0-based in SourceMap spec version 3 + $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine); + $lastOriginalLine = $m['original_line'] - 1; + + $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn); + $lastOriginalColumn = $m['original_column']; + } + } + + $lineMapEncoded[] = $mapEncoded; + } + + $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; + } + + return rtrim(implode($groupedMapEncoded), ';'); + } + + /** + * Finds the index for the filename + * + * @param string $filename + * @return integer|false + */ + protected function findFileIndex($filename){ + return $this->source_keys[$filename]; + } + + /** + * fix windows paths + * @param string $path + * @return string + */ + public function fixWindowsPath($path, $addEndSlash = false){ + $slash = ($addEndSlash) ? '/' : ''; + if( !empty($path) ){ + $path = str_replace('\\', '/', $path); + $path = rtrim($path,'/') . $slash; + } + + return $path; + } + +} + \ No newline at end of file diff --git a/modules/util/Line.class.php b/modules/util/Line.class.php @@ -0,0 +1,152 @@ +<?php + +/** + * Darstellung einer Zeile in einem Freitext.<br> + * <br> + * Im Konstruktor wird die Zeile analysiert und es wird festgestellt, was + * die Zeile f�r einen Inhalt hat (z.B. ein Listenelement, eine �berschrift, usw.)<br> + * + * @author Jan Dankert + * @version $Revision$ + * @package openrat.services + */ +class Line +{ + var $source; // Der urspr�ngliche Inhalt + var $value; // Der textuelle Inhalt (sofern vorhanden) + + var $isDefinition = false; // Definitionseintrag + var $isList = false; // nicht-numerierte Liste + var $isNumberedList = false; // numerierte Liste + var $indent = 0; // Einschubtiefe der Liste + + var $isHeadline = false; // �berschrift + var $isHeadlineUnderline = false; // unterstrichene �berschrift + var $headlineLevel = 0; // Grad der �berschrift (1,2,3...) + + + var $isTableOfContent = false; // Inhaltsverzeichnis + var $isTable = false; // Tabelle + var $isCode = false; // Code + var $isQuote = false; // Zitat + var $isQuotePraefix = false; // Zitatbeginn/-ende + + var $isUnparsed = false; + + var $isEmpty = false; // Zeile ist leer + var $isNormal = false; // Zeile ist ohne besonderen Inhalt, d.h. keine Tabelle, kein Zitat, usw. + + + + /** + * Erzeugt einen Zeilenobjekt, der Text im Parameter wird dabei geparst. + */ + function __construct( $s ) + { + global $conf; + $text_markup = $conf['editor']['text-markup']; +// Html::debug($text_markup); + + $list_numbered = $text_markup['list-numbered' ]; + $list_unnumbered = $text_markup['list-unnumbered']; + $headline = $text_markup['headline' ]; + + $this->source = $s; + $this->value = $s; + + $this->isList = $this->isAnErsterStelle(ltrim($s),$list_unnumbered); + $this->isNumberedList = $this->isAnErsterStelle(ltrim($s),$list_numbered ); + $this->indent = strlen($s) - strlen(ltrim($s)) + 1; + + if ( $this->isList || $this->isNumberedList ) + $this->value = ltrim(substr($s,$this->indent)); + + $this->level = strspn( $s,$headline ); + $this->isHeadline = $this->level >= 1; + + if ( $this->isHeadline ) + $this->value = ltrim(substr($s,$this->level)); + + + $hl = array( 1 => $text_markup['headline_level1_underline'], + 2 => $text_markup['headline_level2_underline'], + 3 => $text_markup['headline_level3_underline'] ); + + foreach($hl as $lev=>$char ) + { + if ( substr($s,0,3*strlen($char))==str_repeat($char,3*strlen($char)) ) + { + $this->isHeadlineUnderline = true; + $this->isNormal = true; + $this->level = intval($lev); + } + } + + if ( $this->isAnErsterStelle($s,$text_markup['table-of-content']) ) + { + $this->isTableOfContent = true; + $this->isNumberedList = false; + $this->isList = false; + $this->value = ''; + } + elseif + ( $this->isHeadline || + $this->isHeadlineUnderline || + $this->isList || + $this->isNumberedList ) + { + ; + } + elseif ( $this->isAnErsterStelle($s,$text_markup['table-cell-sep']) ) + { + $this->isTable = true; + $this->value = ''; + } + elseif ( $this->isAnErsterStelle($s,$text_markup['pre-begin']) && !$this->isHeadlineUnderline ) + { + $this->isCode = true; + $this->value = substr($s,strlen($text_markup['pre-begin'])); + } + elseif ( strpos($s, $text_markup['definition-sep'])!== false ) + { + $this->isDefinition = true; + } + elseif ( trim($s)==$text_markup['quote-line-begin'] ) + { + $this->isQuote = true; + } + elseif ( $this->isAnErsterStelle($s,$text_markup['quote']) && strlen(trim($s))>1 ) + { + $this->isQuotePraefix = true; + $this->value = ltrim(substr($s,strlen($text_markup['quote']))); + $this->level = strspn( str_replace(' ','',$s),$text_markup['quote'] ); + } + elseif ( $this->isAnErsterStelle($s,'`') ) + { + $this->isUnparsed = true; + $this->value = substr($this->value,1); + } + elseif ( $s == '' ) + { + $this->isEmpty = true; + } + else + { + $this->isNormal = true; + } + } + + + /** + * Stellt fest, ob $wort am Anfang von $text steht. + */ + function isAnErsterStelle( $text,$wort ) + { +// Html::debug($text,"Text"); +// Html::debug($wort,"Wort"); +// Html::debug(substr($text,0,strlen($wort))==$wort,'Ergebnis'); + return substr($text,0,strlen($wort))==$wort; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Macro.class.php b/modules/util/Macro.class.php @@ -0,0 +1,194 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +use cms\model\Page; + + +/** + * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere + * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen + * werden. + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Macro +{ + var $project; + var $output = ''; + var $objectid = 0; + + /** + * @var Page + */ + var $page; + var $parameters = array(); + var $description = ''; + + + /** + * Ausführen des Makros. Diese Methode sollte durch die Unterklasse überschrieben werden. + */ + public function execute() + { + // overwrite this in subclasses + } + + + /** + * Holt die aktuellen Datenbankverbindung. + */ + public function db() + { + return db_connection(); + } + + + /** + * Holt die aktuelle Objekt-Id. + * @return number + */ + public function getObjectId() + { + return $this->objectid; + } + + + /** + * Holt die aktuelle Seite. + * @return \cms\model\Page + */ + public function getPage() + { + return new Page( $this->objectid ); + } + + + /** + * Holt das aktuelle Objekt. + * @return Object + */ + public function &getObject() + { + return Session::getObject(); + } + + + /** + * Setzt eine Objekt-Id. + * @param int $objectid + */ + public function setObjectId( $objectid ) + { + $this->objectid = $objectid; + } + + + /** + * Ermittelt die Id des Wurzelordners im aktuellen Projekt. + */ + public function getRootObjectId() + { + $project = Session::getProject(); + return $project->getRootObjectId(); + } + + + /** + * DO NOT USE. + */ + public function folderid() + { + global $SESS; + return $SESS['folderid']; + } + + + /** + * Löscht die bisher erzeugte Ausgabe. + */ + public function delOutput() + { + $this->output = ''; + } + + /** + * Ergänzt die Standardausgabe um den gewünschten Wert. + */ + public function output( $text ) + { + $this->output .= $text; + } + + + /** + * Ergänzt die Standardausgabe um den gewünschten Wert. Es wird ein Zeilenendezeichen ergänzt. + */ + public function outputLn( $text ) + { + $this->output .= $text."\n"; + } + + + /** + * Ermittelt die bisher erstellte Ausgabe. + * @return string + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Setzt eine Sitzungsvariable. + * + * @param String $var + * @param Object $value + */ + public function setSessionVar( $var,$value ) + { + Session::set( $var,$value ); + } + + + /** + * Ermittelt eine Sitzungsvariable. + * + * @param String $var + * @return string + */ + public function getSessionVar( $var ) + { + return Session::get( $var ); + } + + + /** + * Ermittelt den Pfad auf ein Objekt. + * @param Object + * @return string + */ + public function pathToObject( $obj ) + { + if ( is_object($obj) ) + return $this->page->path_to_object($obj->objectid); + else + return $this->page->path_to_object($obj); + } + +}+ \ No newline at end of file diff --git a/modules/util/Mail.class.php b/modules/util/Mail.class.php @@ -0,0 +1,571 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Erzeugen und Versender einer E-Mail gemaess RFC 822.<br> + * <br> + * Die E-Mail kann entweder �ber + * - die interne PHP-Funktion "mail()" versendet werden oder + * - direkt per SMTP-Protokoll an einen SMTP-Server.<br> + * Welcher Weg gew�hlt wird, kann konfiguriert werden.<br> + * <br> + * Prinzipiell spricht nichts gegen die interne PHP-Funktion mail(), wenn diese + * aber nicht zu Verf�gung steht oder PHP ungeeignet konfiguriert ist, so kann + * SMTP direkt verwendet werden. Hierbei sollte wenn m�glich ein Relay-Host + * eingesetzt werden. Die Mail kann zwar auch direkt an Mail-Exchanger (MX) des + * Empf�ngers geschickt werden, falls dieser aber Greylisting einsetzt ist eine + * Zustellung nicht m�glich.<br> + * <br> + * + * @author Jan Dankert + * @version $Id$ + * @package serviceClasses + */ +class Mail +{ + var $from = ''; + var $to = ''; + var $bcc = ''; + var $cc = ''; + var $subject = ''; + var $text = ''; + var $header = array(); + var $nl = ''; + + /** + * Falls beim Versendern der E-Mail etwas schiefgeht, steht hier drin + * die technische Fehlermeldung. + * + * @var String Fehler + */ + var $error = array(); + + /** + * Set to true for debugging. + * If true, All SMTP-Commands are written to error log. + * + * @var unknown_type + */ + var $debug = true; + + + /** + * Konstruktor. + * Es werden folgende Parameter erwartet + * @param String $to Empf�nger + * @param String der Textschl�ssel + * @param String unbenutzt. + * @return Mail + */ + function Mail( $to,$text,$xy='' ) + { + global $conf; + + // Zeilenumbruch CR/LF gem. RFC 822. + $this->nl = chr(13).chr(10); + + if ( !empty($conf['mail']['from']) ) + $this->from = $this->header_encode($conf['mail']['from']); + + // Priorit�t definieren (sofern konfiguriert) + if ( !empty($conf['mail']['priority']) ) + $this->header[] = 'X-Priority: '.$conf['mail']['priority']; + + $this->header[] = 'X-Mailer: '.$this->header_encode(OR_TITLE.' '.OR_VERSION); + $this->header[] = 'Content-Type: text/plain; charset='.lang( 'CHARSET' ); + $this->subject = $this->header_encode(lang( 'mail_subject_'.$text )); + $this->to = $this->header_encode($to); + + $this->text = $this->nl.wordwrap(str_replace(';',$this->nl,lang('mail_text_'.$text)),70,$this->nl).$this->nl; + + // Signatur anhaengen (sofern konfiguriert) + if ( !empty($conf['mail']['signature']) ) + { + $this->text .= $this->nl.'-- '.$this->nl; + $this->text .= str_replace(';',$this->nl,$conf['mail']['signature']); + $this->text .= $this->nl; + } + + // Kopie-Empf�nger + if ( !empty($conf['mail']['cc']) ) + $this->cc = $this->header_encode($conf['mail']['cc']); + + // Blindkopie-Empf�nger + if ( !empty($conf['mail']['bcc']) ) + $this->bcc = $this->header_encode($conf['mail']['bcc']); + } + + + + /** + * Kodiert einen Text in das Format "Quoted-printable".<br> + * See RFC 2045. + * @param String $text Eingabe + * @return Text im quoted-printable-Format + */ + function quoted_printable_encode( $text ) + { + $text = str_replace(' ','=20',$text); + + for( $i=128; $i<=255; $i++ ) + { + $text = str_replace( chr($i),'='.dechex($i),$text ); + } + + return $text; + } + + + + /** + * Setzen einer Variablen in den Mail-Inhalt. + */ + function setVar( $varName,$varInhalt) + { + $this->text = str_replace( '{'.$varName.'}', $varInhalt, $this->text ); + } + + + /** + * Mail absenden. + * Die E-Mail wird versendet. + * + * @return boolean Erfolg + */ + function send() + { + global $conf; + + $to_domain = array_pop( explode('@',$this->to) ); + + // Prüfen gegen die Whitelist + $white = @$conf['mail']['whitelist']; + + if ( !empty($white) ) + { + if ( ! $this->containsDomain($to_domain,$white) ) + { + // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken. + $this->error[] = 'Mail-Domain is not whitelisted'; + return false; + } + } + + // Prüfen gegen die Blacklist + $black = @$conf['mail']['blacklist']; + + if ( !empty($black) ) + { + if ( $this->containsDomain($to_domain,$black) ) + { + // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken. + $this->error[] = 'Mail-Domain is blacklisted'; + return false; + } + } + + // Header um Adressangaben erg�nzen. + if ( !empty($this->from ) ) + $this->header[] = 'From: '.$this->from; + + if ( !empty($this->cc ) ) + $this->header[] = 'Cc: '.$this->cc; + + if ( !empty($this->bcc ) ) + $this->header[] = 'Bcc: '.$this->bcc; + + // Mail versenden + if ( strtolower(@$conf['mail']['client']) == 'php' ) + { + // PHP-interne Mailfunktion verwenden. + $result = @mail( $this->to, // Empf�nger + $this->subject, // Betreff + $this->text, // Inhalt + // Lt. RFC822 müssen Header mit CRLF enden. + // ABER: Der Parameter "additional headers" verlangt offenbar \n am Zeilenende. + implode("\n",$this->header) ); + if ( !$result ) + // Die E-Mail wurde nicht akzeptiert. + // Genauer geht es leider nicht, da mail() nur einen boolean-Wert + // zur�ck liefert. + $this->error[] = 'Mail was NOT accepted by mail()'; + + return $result; + } + else + { + // eigenen SMTP-Dialog verwenden. + $smtpConf = $conf['mail']['smtp']; + + if ( !empty($smtpConf['host'])) + { + // Eigenen Relay-Host verwenden. + $mxHost = $smtpConf['host']; + $mxPort = intval($smtpConf['port']); + } + else + { + // Mail direkt zustellen. + $mxHost = $this->getMxHost($this->to); + + if ( empty($mxHost) ) + { + $this->error[] = "No MX-Entry found. Mail could not be sent."; + return false; + } + + if ($smtpConf['ssl']) + $mxPort = 465; + else + $mxPort = 25; + } + + + if ( !empty($smtpConf['localhost'])) + { + $myHost = $smtpConf['localhost']; + } + else + { + $myHost = gethostbyaddr(getenv('REMOTE_ADDR')); + } + + if ( $smtpConf['ssl']) + $proto = 'ssl'; + else + $proto = 'tcp'; + + //connect to the host and port + $smtpSocket = fsockopen($proto.'://'.$mxHost,$mxPort, $errno, $errstr, intval($smtpConf['timeout'])); + + if ( !is_resource($smtpSocket) ) + { + $this->error[] = 'Connection failed to: '.$proto.'://'.$mxHost.':'.$mxPort.' ('.$errstr.'/'.$errno.')'; + return false; + } + + $smtpResponse = fgets($smtpSocket, 4096); + if ( $this->debug) + $this->error[] = trim($smtpResponse); + + if ( substr($smtpResponse,0,3) != '220' ) + { + $this->error[] = 'No 220: '.trim($smtpResponse); + return false; + } + + if ( !is_resource($smtpSocket) ) + { + $this->error[] = 'Connection failed to: '.$smtpConf['host'].':'.$smtpConf['port'].' ('.$smtpResponse.')'; + return false; + } + + //you have to say HELO again after TLS is started + $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost); + + if ( substr($smtpResponse,0,3) != '250' ) + { + $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + if ( $smtpConf['tls'] ) + { + $smtpResponse = $this->sendSmtpCommand($smtpSocket,'STARTTLS'); + if ( substr($smtpResponse,0,3) == '220' ) + { + // STARTTLS ist gelungen. + //you have to say HELO again after TLS is started + $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost); + + if ( substr($smtpResponse,0,3) != '250' ) + { + $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + } + else + { + // STARTTLS ging in die Hose. Einfach weitermachen. + } + } + + // request for auth login + if ( isset($smtpConf['auth_username']) && !empty($smtpConf['host']) && !empty($smtpConf['auth_username'])) + { + $smtpResponse = $this->sendSmtpCommand($smtpSocket,"AUTH LOGIN"); + if ( substr($smtpResponse,0,3) != '334' ) + { + $this->error[] = "No 334 after AUTH_LOGIN, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + if ( $this->debug) + $this->error[] = 'Login for '.$smtpConf['auth_username']; + + //send the username + $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_username'])); + if ( substr($smtpResponse,0,3) != '334' ) + { + $this->error[] = "No 3xx after setting username, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + //send the password + $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_password'])); + if ( substr($smtpResponse,0,3) != '235' ) + { + $this->error[] = "No 235 after sending password, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + } + + //email from + $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'MAIL FROM: <'.$conf['mail']['from'].'>'); + if ( substr($smtpResponse,0,3) != '250' ) + { + $this->error[] = "No 2xx after MAIL_FROM, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + //email to + $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'RCPT TO: <'.$this->to.'>'); + if ( substr($smtpResponse,0,3) != '250' ) + { + $this->error[] = "No 2xx after RCPT_TO, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + //the email + $smtpResponse = $this->sendSmtpCommand($smtpSocket, "DATA"); + if ( substr($smtpResponse,0,3) != '354' ) + { + $this->error[] = "No 354 after DATA, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + $this->header[] = 'To: '.$this->to; + $this->header[] = 'Subject: '.$this->subject; + $this->header[] = 'Date: '.date('r'); + $this->header[] = 'Message-Id: '.'<'.getenv('REMOTE_ADDR').'.'.time().'.openrat@'.getenv('SERVER_NAME').'.'.getenv('HOSTNAME').'>'; + + //observe the . after the newline, it signals the end of message + $smtpResponse = $this->sendSmtpCommand($smtpSocket, implode($this->nl,$this->header).$this->nl.$this->nl.$this->text.$this->nl.'.'); + if ( substr($smtpResponse,0,3) != '250' ) + { + $this->error[] = "No 2xx after putting DATA, server says: ".$smtpResponse; + $this->sendSmtpQuit($smtpSocket); + return false; + } + + // say goodbye + $this->sendSmtpQuit($smtpSocket); + return true; + } + } + + + /** + * Sendet ein SMTP-Kommando zum SMTP-Server. + * + * @access private + * @param Resource $socket TCP/IP-Socket zum SMTP-Server + * @param unknown_type $cmd SMTP-Kommando + * @return Server-Antwort + */ + function sendSmtpCommand( $socket,$cmd ) + { + if ( $this->debug ) + $this->error[] = 'CLIENT: >>> '.trim($cmd); + if ( !is_resource($socket) ) + { + // Die Verbindung ist geschlossen. Dies kann bei dieser + // Implementierung eigentlich nur dann passieren, wenn + // der Server die Verbindung schlie�t. + // Dieser Client trennt die Verbindung nur nach einem "QUIT". + $this->error[] = "Connection lost"; + return; + } + + fputs($socket,$cmd.$this->nl); + $response = trim(fgets($socket, 4096)); + if ( $this->debug ) + $this->error[] = 'SERVER: <<< '.$response; + return $response; + } + + + + /** + * Sendet ein QUIT zum SMTP-Server, wartet die Antwort ab und + * schlie�t danach die Verbindung. + * + * @param Resource Socket + */ + function sendSmtpQuit( $socket ) + { + + if ( $this->debug ) + $this->error[] = "CLIENT: >>> QUIT"; + if ( !is_resource($socket) ) + return; + // Wenn die Verbindung nicht mehr da ist, brauchen wir + // auch kein QUIT mehr :) + + + fputs($socket,'QUIT'.$this->nl); + $response = trim(fgets($socket, 4096)); + if ( $this->debug ) + $this->error[] = 'SERVER: <<< '.$response; + + if ( substr($response,0,3) != '221' ) + $this->error[] = 'QUIT FAILED: '.$response; + + fclose($socket); + } + + + + /** + * Umwandlung von 8-bit-Zeichen in MIME-Header gemaess RFC 2047.<br> + * Header d�rfen nur 7-bit-Zeichen enthalten. 8-bit-Zeichen m�ssen kodiert werden. + * + * @param String $text + * @return String + */ + function header_encode( $text ) + { + global $conf; + + if ( empty($conf['mail']['header_encoding']) ) + return $text; + + $woerter = explode(' ',$text); + $neu = array(); + + + foreach( $woerter as $wort ) + { + $type = strtolower(substr($conf['mail']['header_encoding'],0,1)); + $neu_wort = ''; + + if ( $type == 'b' ) + $neu_wort = base64_encode($wort); + elseif ( $type == 'q' ) + $neu_wort = $this->quoted_printable_encode($wort); + else + Logger::error( 'Mail-Configuratin broken: UNKNOWN Header-Encoding type: '.$type); + + if ( strlen($wort)==strlen($neu_wort) ) + $neu[] = $wort; + else + $neu[] = '=?'.lang('CHARSET').'?'.$type.'?'.$neu_wort.'?='; + } + + return implode(' ',$neu); + } + + + /** + * Ermittelt den MX-Eintrag zu einer E-Mail-Adresse.<br> + * Es wird der Eintrag mit der h�chsten Priorit�t ermittelt. + * + * @param String E-Mail-Adresse des Empf�ngers. + * @return MX-Eintrag + */ + function getMxHost( $to ) + { + list($user,$host) = explode('@',$to.'@'); + + if ( empty($host) ) + { + $this->error[] = 'Illegal mail address - No hostname found.'; + return ""; + } + + list($host) = explode('>',$host); + + $mxHostsName = array(); + $mxHostsPrio = array(); + getmxrr($host,$mxHostsName,$mxHostsPrio); + + $mxList = array(); + foreach( $mxHostsName as $id=>$mxHostName ) + { + $mxList[$mxHostName] = $mxHostsPrio[$id]; + } + asort($mxList); + return key($mxList); + } + + + + /** + * Stellt fest, ob die E-Mail-Adresse eine gueltige Syntax besitzt. + * + * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange + * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen. + * + * @param $email_address Adresse + * @return true, falls Adresse OK, sonst false + */ + function checkAddress( $email_address ) + { + // Source: de.php.net/ereg + return ereg("^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$", $email_address); + } + + + + /** + * Prüft, ob eine Domain in einer List von Domains enthalten ist. + * + * @param $checkDomain zu prüfende Domain + * @param $domain_list Liste von Domains als kommaseparierte Liste + * @return true, falls vorhanden, sonst false + */ + function containsDomain($checkDomain, $domain_list) + { + $domains = explode(',',$domain_list); + + foreach( $domains as $domain ) + { + $domain = trim($domain); + + if (empty($domain)) + continue; + + if ($domain == substr($checkDomain,-strlen($domain))) + { + return true; + } + } + return false; + } +} + + +?> diff --git a/modules/util/ProjectTree.class.php b/modules/util/ProjectTree.class.php @@ -0,0 +1,515 @@ +<?php +use cms\model\Value; +use cms\model\Element; +use cms\model\Template; +use cms\model\Page; +use cms\model\Folder; +use cms\model\Object; +use cms\model\File; +use cms\model\Link; + +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Darstellen der Projektstruktur + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class ProjectTree extends AbstractTree +{ + var $projectId; + var $userIsProjectAdmin = false; + + function root() + { + $treeElement = new TreeElement(); + $treeElement->text = lang('GLOBAL_PROJECT'); + $treeElement->description = lang('GLOBAL_PROJECT'); + $treeElement->type = 'project'; + $treeElement->icon = 'project'; + + $this->addTreeElement( $treeElement ); + } + + + + function page( $id ) + { + $page = new Page( $id ); + $page->load(); + + $template = new Template( $page->templateid ); + + foreach( $template->getElementIds() as $elementid ) + { + $element = new Element( $elementid ); + $element->load(); + + if ( $element->isWritable() ) + { + $treeElement = new TreeElement(); + $treeElement->id = $id.'_'.$elementid; + $treeElement->extraId['elementid'] = $elementid; + $treeElement->text = $element->name; + $treeElement->url = Html::url('pageelement','edit', + $id.'_'.$elementid, + array('elementid'=>$elementid, + REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'pageelement'; + $treeElement->icon = 'el_'.$element->type; + + $treeElement->description = lang('EL_'.$element->type); + if ( $element->desc != '' ) + $treeElement->description .= ' - '.Text::maxLaenge( 25,$element->desc ); + else + $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + $treeElement->target = 'content'; + + if ( in_array($element->type,array('link','list','include') ) ) + { + $treeElement->type = 'value'; + $value = new Value(); + $value->pageid = $page->pageid; + $value->element = $element; + $value->load(); + $treeElement->internalId = $value->valueid; + } + + $this->addTreeElement( $treeElement ); + } + } + } + + + function value( $id ) + { + //echo "id: $id"; + if ( $id != 0 ) + { + $value = new Value(); + $value->loadWithId( $id ); + + $objectid = intval($value->linkToObjectId); + if ( $objectid != 0 ) + { + $object = new Object( $objectid ); + $object->load(); + + $treeElement = new TreeElement(); + $treeElement->id = $id; + $treeElement->text = $object->name; + if ( in_array($object->getType(),array('page','folder'))) + { + $treeElement->type = $object->getType(); + $treeElement->internalId = $object->objectid; + } + $treeElement->url = Html::url($object->getType(),'',$objectid,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = $object->getType(); + $treeElement->icon = $object->getType(); + + $treeElement->description = lang('GLOBAL_'.$object->getType()); + if ( $object->desc != '' ) + $treeElement->description .= ' - '.Text::maxLaenge( 25,$object->desc ); + else + $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + $treeElement->target = 'content'; + + $this->addTreeElement( $treeElement ); + } + } + } + + + function link( $id ) + { + $link = new Link( $id ); + $link->load(); + + if ( $link->isLinkToObject ) + { + $o = new Object( $link->linkedObjectId ); + $o->load(); + + $treeElement = new TreeElement(); + $treeElement->id = $o->objectid; + $treeElement->internalId = $o->objectid; + $treeElement->target = 'content'; + $treeElement->text = $o->name; + $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$id; + + if ( $o->desc != '' ) + $treeElement->description .= ': '.$o->desc; + else + $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + + $treeElement->url = Html::url($o->getType(),'',$o->objectid,array(REQ_PARAM_TARGET=>'content') ); + $treeElement->action = $o->getType(); + $treeElement->icon = $o->getType(); + + // Besonderheiten fuer bestimmte Objekttypen + + if ( $o->isPage ) + { + // Nur wenn die Seite beschreibbar ist, werden die + // Elemente im Baum angezeigt + if ( $o->hasRight( ACL_WRITE ) ) + $treeElement->type='pageelements'; + } + $this->addTreeElement( $treeElement ); + } + } + + + /** + * Laedt Elemente zu einem Ordner + * @return Array + */ + function folder( $id ) + { + global + $SESS, + $projectid; + + $f = new Folder( $id ); + $t = time(); + + foreach( $f->getObjects() as $o ) + { + // Wenn keine Leseberechtigung + if ( !$o->hasRight( ACL_READ ) ) + continue; + + $treeElement = new TreeElement(); + $treeElement->id = $o->objectid; + $treeElement->internalId = $o->objectid; + $treeElement->target = 'content'; + $treeElement->text = $o->name; + $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$o->objectid; + + if ( $o->desc != '' ) + $treeElement->description .= ': '.$o->desc; + else + $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + + $treeElement->url = Html::url( $o->getType(),'',$o->objectid,array('readit'=>'__OID__'.$o->objectid.'__',REQ_PARAM_TARGET=>'content') ); + $treeElement->action = $o->getType(); + $treeElement->icon = $o->getType(); + + // Besonderheiten fuer bestimmte Objekttypen + + if ( $o->isLink ) + { + $treeElement->type='link'; + } + + if ( $o->isPage ) + { + // Nur wenn die Seite beschreibbar ist, werden die + // Elemente im Baum angezeigt + if ( $o->hasRight( ACL_WRITE ) ) + $treeElement->type='page'; + } + + if ( $o->isFile ) + { + $file = new File( $o->objectid ); + $file->load(); + + if ( substr($file->mimeType(),0,6) == 'image/' ) + $treeElement->icon = 'image'; + else $treeElement->icon = 'file'; + } + + if ( $o->isFolder ) + { + $treeElement->type = 'folder'; + } + + + $this->addTreeElement( $treeElement ); + } + } + + + function project() + { + $language = Session::getProjectLanguage(); + $model = Session::getProjectModel(); + $user = Session::getUser(); + + $project = Session::getProject(); + $this->projectid = $project->projectid; + + // Hoechster Ordner der Projektstruktur + $folder = new Folder( $project->getRootObjectId() ); + $folder->load(); + + + // Ermitteln, ob der Benutzer Projektadministrator ist + // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern. + if ( $folder->hasRight( ACL_PROP ) ) + $this->userIsProjectAdmin = true; + + if ( $folder->hasRight( ACL_READ ) ) + { + $treeElement = new TreeElement(); + $treeElement->id = $folder->objectid; + // $treeElement->text = $folder->name; + $treeElement->text = lang('FOLDER_ROOT'); + $treeElement->description = lang('FOLDER_ROOT_DESC'); + $treeElement->icon = 'folder'; + $treeElement->action = 'folder'; + $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') ); + $treeElement->target = 'content'; + $treeElement->type = 'folder'; + $treeElement->internalId = $folder->objectid; + $this->addTreeElement( $treeElement ); + } + + + if ( $this->userIsProjectAdmin ) + { + // Templates + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_TEMPLATES'); + $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->description= lang('GLOBAL_TEMPLATES_DESC'); + $treeElement->icon = 'templatelist'; + $treeElement->action = 'templatelist'; + $treeElement->target = 'content'; + $treeElement->type = 'templates'; + $this->addTreeElement( $treeElement ); + } + + + // Sprachen + $treeElement = new TreeElement(); + $treeElement->description= ''; + $treeElement->id = 0; + $treeElement->action = 'languagelist'; + $treeElement->text = lang('GLOBAL_LANGUAGES'); + $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'languagelist'; + $treeElement->description= lang('GLOBAL_LANGUAGES_DESC'); + $treeElement->target = 'content'; + + // Nur fuer Projekt-Administratoren aufklappbar + if ( $this->userIsProjectAdmin ) + $treeElement->type = 'languages'; + + $this->addTreeElement( $treeElement ); + + + // Projektmodelle + $treeElement = new TreeElement(); + $treeElement->description= ''; + + // Nur fuer Projekt-Administratoren aufklappbar + if ( $this->userIsProjectAdmin ) + $treeElement->type = 'models'; + + $treeElement->id = 0; + $treeElement->description= lang('GLOBAL_MODELS_DESC'); + $treeElement->text = lang('GLOBAL_MODELS'); + $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'modellist'; + $treeElement->icon = 'modellist'; + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + + // Sonstiges +// $treeElement = new TreeElement(); +// $treeElement->text = lang('GLOBAL_OTHER'); +// $treeElement->description= lang('GLOBAL_OTHER_DESC'); +// $treeElement->icon = 'other'; +// $treeElement->type = 'other'; +// $this->addTreeElement( $treeElement ); + + // Suche + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_SEARCH'); + $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'search'; + $treeElement->icon = 'search'; + $treeElement->description = lang('GLOBAL_SEARCH_DESC'); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + } + + + function templates() + { + foreach( Template::getAll() as $id=>$name ) + { + $treeElement = new TreeElement(); + + $t = new Template( $id ); + $t->load(); + $treeElement->text = $t->name; + $treeElement->id = $id; + $treeElement->url = Html::url('template','src',$id,array(REQ_PARAM_TARGETSUBACTION=>'src',REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'template'; + $treeElement->action = 'template'; + $treeElement->target = 'content'; + $treeElement->internalId = $id; + $treeElement->type = 'template'; + $treeElement->description = $t->name.' ('.lang('GLOBAL_TEMPLATE').' '.$id.'): '.htmlentities(Text::maxLaenge( 40,$t->src )); + $this->addTreeElement( $treeElement ); + } + } + + + function template( $id ) + { + + $t = new Template( $id ); + $t->load(); + + // Anzeigen der Template-Elemente + // + foreach( $t->getElementIds() as $elementid ) + { + $e = new Element( $elementid ); + $e->load(); + + // "Code"-Element nur fuer Administratoren + if ( $e->type == 'code' && !$this->userIsAdmin ) + continue; + + $treeElement = new TreeElement(); + $treeElement->id = $elementid; + $treeElement->text = $e->name; + $treeElement->url = Html::url('element','',$elementid,array(REQ_PARAM_TARGET=>'content') ); + $treeElement->icon = 'el_'.$e->type; + $treeElement->action = 'element'; + + if ( $e->desc == '' ) + $desc = lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); + else + $desc = $e->desc; + $treeElement->description = $e->name.' ('.lang('EL_'.$e->type).'): '.Text::maxLaenge( 40,$desc ); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + } + } + + + /** + * Sprachen + */ + function languages() + { + // Sprachvarianten + // + $l = Session::getProjectLanguage(); + $languages = $l->getAll(); + + foreach( $languages as $languageid=>$name ) + { + $treeElement = new TreeElement(); + $treeElement->id = $languageid; + $treeElement->text = $name; + $treeElement->url = Html::url('language','edit',$languageid, + array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content') ); + $treeElement->icon = 'language'; + $treeElement->action = 'language'; + $treeElement->description = ''; + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + } + } + + + // Projektvarianten + // + function models() + { + $m = Session::getProjectModel(); + $models = $m->getAll(); + + foreach( $models as $id=>$name ) + { + $treeElement = new TreeElement(); + $treeElement->id = $id; + $treeElement->text = $name; + $treeElement->url = Html::url('model','edit',$id, + array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content')); + $treeElement->action = 'model'; + $treeElement->icon = 'model'; + $treeElement->description = ''; + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + } + } + + + function other() + { +// Deaktiviert, da +// - Dateien auf den Server laden unverst�ndlich/undurchsichtig erscheint +// - M�glichkeit zum Entpacken von ZIP/TAR online besteht. +// if ( $this->userIsProjectAdmin ) +// { +// $treeElement = new TreeElement(); +// $treeElement->text = lang('GLOBAL_FILE_TRANSFER'); +// $treeElement->description = lang('GLOBAL_FILE_TRANSFER_DESC'); +// $treeElement->url = Html::url('main','transfer'); +// $treeElement->icon = 'transfer'; +// $treeElement->target = 'content'; +// $this->addTreeElement( $treeElement ); +// } + + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_SEARCH'); + $treeElement->url = Html::url('search'); + $treeElement->icon = 'search'; + $treeElement->action = 'search'; + $treeElement->description = lang('GLOBAL_SEARCH_DESC'); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('USER_YOURPROFILE'); + $treeElement->url = Html::url('profile','edit',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'user'; + $treeElement->action = 'profile'; + $treeElement->description = lang('USER_PROFILE_DESC'); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + + + $treeElement = new TreeElement(); + $treeElement->id = 0; + $treeElement->text = lang('GLOBAL_PROJECTS'); + $treeElement->url = Html::url('index','projectmenu',0,array(REQ_PARAM_TARGET=>'content')); + $treeElement->icon = 'project'; + $treeElement->description = lang('GLOBAL_PROJECTS'); + $treeElement->target = 'content'; + $this->addTreeElement( $treeElement ); + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Publish.class.php b/modules/util/Publish.class.php @@ -0,0 +1,393 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Diese Klasse kapselt das Veroeffentlichen von Dateien.<br> + * <br> + * Hier werden<br> + * - Dateien in das Zielverzeichnis kopiert<br> + * - Dateien per FTP veroeffentlicht<br> + * - Zielverzeichnisse aufgeraeumt<br> + * - Systembefehle ausgefuehrt. + * + * @author Jan Dankert + * @package openrat.services + */ +class Publish +{ + /** + * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn + * zu einem FTP-Server veroeffentlicht werden soll. + * @var Object + */ + var $ftp; + + /** + * Flag, ob in das lokale Dateisystem veroeffentlicht werden soll. + * @var boolean + */ + var $with_local = false; + + /** + * Flag, ob zu einem FTP-Server ver�ffentlicht werden soll. + * @var boolean + */ + var $with_ftp = false; + + var $local_destdir = ''; + + /** + * Enthaelt die gleichnamige Einstellung aus dem Projekt. + * @var boolean + */ + var $content_negotiation = false; + + /** + * Enthaelt die gleichnamige Einstellung aus dem Projekt. + * @var boolean + */ + var $cut_index = false; + + /** + * Enthaelt die gleichnamige Einstellung aus dem Projekt. + * @var String + */ + var $cmd_after_publish = ''; + + /** + * Enthaelt am Ende der Ver�ffentlichung ein Array mit den ver�ffentlichten Objekten. + * @var Array + */ + var $publishedObjects = array(); + + /** + * Enthaelt im Fehlerfall (wenn 'ok' auf 'false' steht) eine + * Fehlermeldung. + * + * @var String + */ + var $log = array(); + + /** + * Stellt nach der Ver�ffentlichung fest, ob der Vorgang erfolgreich ist. + * Falls nicht, enth�lt die Variable 'log' eine Fehlermeldung. + * @var boolean + */ + var $ok = true; + + /** + * Konstruktor.<br> + * <br> + * Oeffnet ggf. Verbindungen. + * + * @return Publish + */ + function __construct() + { + global $conf; + $confPublish = $conf['publish']; + + if ( $conf['security']['nopublish'] ) + { + $this->ok = false; + $this->log[] = 'publishing is disabled.'; + return; + } + + $project = Session::getProject(); + + // Feststellen, ob FTP benutzt wird. + // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein. + $ftpUrl = ''; + if ( $conf['publish']['ftp']['enable'] ) + { + if ( $conf['publish']['ftp']['per_project'] && !empty($project->ftp_url) ) + $ftpUrl = $project->ftp_url; + elseif ( !empty($conf['publish']['ftp']['host']) ) + $ftpUrl = $project->ftp_url; + } + + if ( ! empty($ftpUrl) ) + { + $this->with_ftp = true; + $this->ftp = new Ftp( $project->ftp_url ); // Aufbauen einer FTP-Verbindung + + if ( ! $this->ftp->ok ) // FTP-Verbindung ok? + { + $this->ok = false; + $this->log = $this->ftp->log; + return; // Ende. Ohne FTP brauchen wir nicht weitermachen. + } + + $this->ftp->passive = ( $project->ftp_passive == '1' ); + } + + $localDir = rtrim( $project->target_dir,'/' ); + + if ( $confPublish['filesystem']['per_project'] && (!empty($localDir)) ) + { + $this->local_destdir = $localDir; // Projekteinstellung verwenden. + } + else + { + if ( empty( $localDir)) + $localDir = $project->name; + // Konfiguriertes Verzeichnis verwenden. + $this->local_destdir = $confPublish['filesystem']['directory'].$localDir; + } + + + // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist. + if ( $this->local_destdir != '' ) + { + if ( !is_writeable( $this->local_destdir ) ) + { + $this->ok = false; + $this->log[] = 'directory not writable: '.$this->local_destdir; + $this->log[] = 'please correct the file permissions.'; + return; + } + + $this->with_local = true; + } + + $this->content_negotiation = ( $project->content_negotiation == '1' ); + $this->cut_index = ( $project->cut_index == '1' ); + + if ( $confPublish['command']['enable'] ) + { + if ( $confPublish['command']['per_project'] && !empty($project->cmd_after_publish) ) + $this->cmd_after_publish = $project->cmd_after_publish; + else + $this->cmd_after_publish = @$confPublish['command']['command']; + } + + // Im Systemkommando Variablen ersetzen + $this->cmd_after_publish = str_replace('{name}' ,$project->name ,$this->cmd_after_publish); + $this->cmd_after_publish = str_replace('{dir}' ,$this->local_destdir ,$this->cmd_after_publish); + $this->cmd_after_publish = str_replace('{dirbase}',basename($this->local_destdir),$this->cmd_after_publish); + } + + + + /** + * Kopieren einer Datei aus dem tempor�ren Verzeichnis in das Zielverzeichnis.<br> + * Falls notwenig, wird ein Hochladen per FTP ausgef�hrt. + * + * @param String $tmp_filename + * @param String $dest_filename + */ + function copy( $tmp_filename,$dest_filename,$lastChangeDate=null ) + { + if ( !$this->ok) + return; + + global $conf; + $source = $tmp_filename; + + if ( $this->with_local ) + { + $dest = $this->local_destdir.'/'.$dest_filename; + + if (!@copy( $source,$dest )); + { + if ( ! $this->mkdirs( dirname($dest) ) ) + return; // Fehler bei Verzeichniserstellung, also abbrechen. + + if (!@copy( $source,$dest )) + { + $this->ok = false; + $this->log[] = 'failed copying local file:'; + $this->log[] = 'source : '.$source; + $this->log[] = 'destination: '.$dest; + return; // Fehler beim Kopieren, also abbrechen. + } + if ( ! is_null($lastChangeDate) ) + @touch( $dest,$lastChangeDate ); + + Logger::debug("published: $dest"); + } + + if (!empty($conf['security']['chmod'])) + { + // CHMOD auf der Datei ausfuehren. + if ( ! @chmod($dest,octdec($conf['security']['chmod'])) ) + { + $this->ok = false; + $this->log[] = 'Unable to CHMOD file '.$dest; + return; + } + } + } + + if ( $this->with_ftp ) // Falls FTP aktiviert + { + $dest = $dest_filename; + $this->ftp->put( $source,$dest ); + + if ( ! $this->ftp->ok ) + { + $this->ok = false; + $this->log[] = $this->ftp->log; + } + } + } + + + + /** + * Rekursives Anlagen von Verzeichnisse + * Nett gemacht. + * Quelle: http://de3.php.net/manual/de/function.mkdir.php + * Thx to acroyear at io dot com + * + * @param String Verzeichnis + * @return boolean + */ + function mkdirs( $strPath ) + { + global $conf; + + if ( is_dir($strPath) ) + return true; + + $pStrPath = dirname($strPath); + if ( !$this->mkdirs($pStrPath) ) + return false; + + if ( ! @mkdir($strPath,0777) ) + { + $this->ok = false; + $this->log[] = 'Cannot create directory: '.$strPath; + return false; + } + + // CHMOD auf dem Verzeichnis ausgef�hren. + if (!empty($conf['security']['chmod_dir'])) + { + if ( ! @chmod($strPath,octdec($conf['security']['chmod_dir'])) ) + { + $this->ok = false; + $this->log[] = 'Unable to CHMOD directory: '.$strPath; + return false; + } + } + + + return $this->ok; + } + + + + /** + * Beenden des Ver�ffentlichungs-Vorganges.<br> + * Eine vorhandene FTP-Verbindung wird geschlossen.<br> + * Falls entsprechend konfiguriert, wird ein Systemkommando ausgef�hrt. + */ + public function close() + { + if ( $this->with_ftp ) + { + Logger::debug('Closing FTP connection' ); + $this->ftp->close(); + } + + // Ausfuehren des Systemkommandos. + if ( !empty($this->cmd_after_publish) && $this->ok ) + { + $ausgabe = array(); + $rc = false; + Logger::debug('Executing system command: '.$this->cmd_after_publish ); + $user = Session::getUser(); + putenv("CMS_USER_NAME=".$user->name ); + putenv("CMS_USER_ID=" .$user->userid); + putenv("CMS_USER_MAIL=".$user->mail ); + exec( $this->cmd_after_publish,$ausgabe,$rc ); + + if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Ausgabe ins Log schreiben und Fehler melden. + { + $this->log = $ausgabe; + $this->log[] = 'OpenRat: System command failed - returncode is '.$rc; + $this->ok = false; + + Logger::warn('System command '.$this->cmd_after_publish.' failed with status '.$rc ); + + } + else + { + Logger::debug('System command successful' ); + } + + } + } + + + + /** + * Aufraeumen des Zielverzeichnisses.<br><br> + * Es wird der komplette Zielordner samt Unterverzeichnissen durchsucht. Jede + * Datei, die laenger existiert als der aktuelle Request alt ist, wird geloescht.<br> + * Natuerlich darf diese Funktion nur nach einem Gesamt-Veroeffentlichen ausgefuehrt werden. + */ + function clean() + { + if ( $this->ok ) + return; + + if ( !empty($this->local_destdir) ) + $this->cleanFolder($this->local_destdir); + } + + + + /** + * Aufr�umen eines Verzeichnisses.<br><br> + * Dateien, die l�nger existieren als der aktuelle Request alt ist, werden gel�scht.<br> + * + * @param String Verzeichnis + */ + function cleanFolder( $folderName ) + { + $dh = opendir( $folderName ); + + while( $file = readdir($dh) ) + { + if ( $file != '.' && $file != '..') + { + $fullpath = $folderName.'/'.$file; + + // Wenn eine Datei beschreibbar und entsprechend alt + // ist, dann entfernen + if ( is_file($fullpath) && + is_writable($fullpath) && + filemtime($fullpath) < START_TIME ) + unlink($fullpath); + + // Bei Ordnern rekursiv absteigen + if ( is_dir( $fullpath) ) + { + $this->cleanFolder($fullpath); + @rmdir($fullpath); + } + } + } + } + +} + +?>+ \ No newline at end of file diff --git a/modules/util/Session.class.php b/modules/util/Session.class.php @@ -0,0 +1,242 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +// PHP-Versionsunabhaengiges Array fuer die Session-Variablen ermitteln +use cms\model\User; + +if (isset($_SESSION)) + $SESS = &$_SESSION; +else $SESS = &$HTTP_SESSION_VARS; + +if ( isset($_FILES) ) + $FILES = &$_FILES; +else $FILES = &$HTTP_POST_FILES; + + +/** + * Session-Funktionen zum Lesen/Schreiben in/von HTTP-Session + * In der Session werden folgende Daten abgelegt + * - Ausgewaehltes Projekt + * - Ausgewaehlte Projectsprache + * - Ausgewaehlte Projektvariante + * - Angemeldeter Benutzer + * - Auswahlbaum + * - Geladene Sprachelemente + * - Ausgewaehlter Ordner + * - Ausgewaehltes Objekt + * - Datenbankobjekt + * Die Methoden dieser Klassen koennen statisch aufgerufen werden + * + * @author $Author$ + * @version $Revision$ + * @package openrat.service + */ + +class Session +{ + public static function get( $var ) + { + global $SESS; + if ( isset($SESS['ors_'.$var]) ) + return $SESS['ors_'.$var]; + else + return ''; + } + + public static function set( $var,$value ) + { + global $SESS; + $SESS[ 'ors_'.$var ] = $value; + } + + + /** + * @return array + */ + public static function getConfig() + { + return Session::get('config'); + } + + public static function setConfig( $var ) + { + Session::set('config',$var); + } + + + /** + * @return \cms\model\Model + */ + public static function getProjectModel() + { + return Session::get('project_model'); + } + + public static function setProjectModel( $var ) + { + Session::set('project_model',$var); + } + + + /** + * @return \cms\model\Language + */ + public static function getProjectLanguage() + { + return Session::get('project_language'); + } + + public static function setProjectLanguage( $var ) + { + Session::set('project_language',$var); + } + + + + public static function getObject() + { + return Session::get('object'); + } + + public static function setObject( $var ) + { + Session::set('object',$var); + } + + + /** + * @return \cms\model\Folder + */ + public static function getFolder() + { + return Session::get('folder'); + } + + public static function setFolder( $var ) + { + Session::set('folder',$var); + } + + + + public static function getTree() + { + return Session::get('tree'); + } + + public static function setTree( $var ) + { + Session::set('tree',$var); + } + + + + public static function getElement() + { + return Session::get('element'); + } + + public static function setElement( $var ) + { + Session::set('element',$var); + } + + + /** + * @return \cms\model\Project + */ + public static function getProject() + { + return Session::get('project'); + } + + public static function setProject( $var ) + { + Session::set('project',$var); + } + + + /** + * @return User + */ + public static function getUser() + { + return Session::get('userObject'); + } + + public static function setUser( $var ) + { + Session::set('userObject',$var); + } + + + /** + * @return \database\Database + */ + public static function getDatabase() + { + return Session::get('database'); + } + + public static function setDatabase( $var ) + { + Session::set('database',$var); + } + + + /** + * @return string + */ + public static function getSubaction() + { + return Session::get('subaction'); + } + + public static function setSubaction( $var ) + { + Session::set('subaction',$var); + } + + + public static function getClipboard() + { + return Session::get('clipboard'); + } + + public static function setClipboard( $var ) + { + Session::set('clipboard',$var); + } + + + /** + * Schliesst die aktuelle Session + * + * Diese Funktion sollte so schnell wie moeglich aufgerufen werden, da vorher + * keine andere Seite (im Frameset!) geladen werden kann + * Nach Aufruf dieser Methode sind keine Session-Zugriffe ueber diese Klasse mehr + * moeglich. + */ + public static function close() + { + session_write_close(); + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Spyc.class.php b/modules/util/Spyc.class.php @@ -0,0 +1,1161 @@ +<?php +/** + * Spyc -- A Simple PHP YAML Class + * @version 0.6.2 + * @author Vlad Andersen <vlad.andersen@gmail.com> + * @author Chris Wanstrath <chris@ozmm.org> + * @link https://github.com/mustangostang/spyc/ + * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + +if (!function_exists('spyc_load')) { + /** + * Parses YAML to array. + * @param string $string YAML string. + * @return array + */ + function spyc_load ($string) { + return Spyc::YAMLLoadString($string); + } +} + +if (!function_exists('spyc_load_file')) { + /** + * Parses YAML to array. + * @param string $file Path to YAML file. + * @return array + */ + function spyc_load_file ($file) { + return Spyc::YAMLLoad($file); + } +} + +if (!function_exists('spyc_dump')) { + /** + * Dumps array to YAML. + * @param array $data Array. + * @return string + */ + function spyc_dump ($data) { + return Spyc::YAMLDump($data, false, false, true); + } +} + +if (!class_exists('Spyc')) { + +/** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * <code> + * $Spyc = new Spyc; + * $array = $Spyc->load($file); + * </code> + * or: + * <code> + * $array = Spyc::YAMLLoad($file); + * </code> + * or: + * <code> + * $array = spyc_load_file($file); + * </code> + * @package Spyc + */ +class Spyc { + + // SETTINGS + + const REMPTY = "\0\0\0\0\0"; + + /** + * Setting this to true will force YAMLDump to enclose any string value in + * quotes. False by default. + * + * @var bool + */ + public $setting_dump_force_quotes = false; + + /** + * Setting this to true will forse YAMLLoad to use syck_load function when + * possible. False by default. + * @var bool + */ + public $setting_use_syck_is_possible = false; + + + + /**#@+ + * @access private + * @var mixed + */ + private $_dumpIndent; + private $_dumpWordWrap; + private $_containsGroupAnchor = false; + private $_containsGroupAlias = false; + private $path; + private $result; + private $LiteralPlaceHolder = '___YAML_Literal_Block___'; + private $SavedGroups = array(); + private $indent; + /** + * Path modifier that should be applied after adding current element. + * @var array + */ + private $delayedPath = array(); + + /**#@+ + * @access public + * @var mixed + */ + public $_nodeId; + +/** + * Load a valid YAML string to Spyc. + * @param string $input + * @return array + */ + public function load ($input) { + return $this->_loadString($input); + } + + /** + * Load a valid YAML file to Spyc. + * @param string $file + * @return array + */ + public function loadFile ($file) { + return $this->_load($file); + } + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * <code> + * $array = Spyc::YAMLLoad('lucky.yaml'); + * print_r($array); + * </code> + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + public static function YAMLLoad($input) { + $Spyc = new Spyc; + return $Spyc->_load($input); + } + + /** + * Load a string of YAML into a PHP array statically + * + * The load method, when supplied with a YAML string, will do its best + * to convert YAML in a string into a PHP array. Pretty simple. + * + * Note: use this function if you don't want files from the file system + * loaded and processed as YAML. This is of interest to people concerned + * about security whose input is from a string. + * + * Usage: + * <code> + * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); + * print_r($array); + * </code> + * @access public + * @return array + * @param string $input String containing YAML + */ + public static function YAMLLoadString($input) { + $Spyc = new Spyc; + return $Spyc->_loadString($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array|\stdClass $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + * @param bool $no_opening_dashes Do not start YAML file with "---\n" + */ + public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) { + $spyc = new Spyc; + return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes); + } + + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yaml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = ""; + if (!$no_opening_dashes) $string = "---\n"; + + // Start at the base of the array and move through it. + if ($array) { + $array = (array)$array; + $previous_key = -1; + foreach ($array as $key => $value) { + if (!isset($first_key)) $first_key = $key; + $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array); + $previous_key = $key; + } + } + return $string; + } + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) { + if(is_object($value)) $value = (array)$value; + if (is_array($value)) { + if (empty ($value)) + return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array); + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + private function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + $previous_key = -1; + foreach ($array as $key => $value) { + if (!isset($first_key)) $first_key = $key; + $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array); + $previous_key = $key; + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) { + // do some folding here, for blocks + if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || + strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false || + strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 || + substr ($value, -1, 1) == ':') + ) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + } + + if ($value === array()) $value = '[ ]'; + if ($value === "") $value = '""'; + if (self::isTranslationWord($value)) { + $value = $this->_doLiteralBlock($value, $indent); + } + if (trim ($value) != $value) + $value = $this->_doLiteralBlock($value,$indent); + + if (is_bool($value)) { + $value = $value ? "true" : "false"; + } + + if ($value === null) $value = 'null'; + if ($value === "'" . self::REMPTY . "'") $value = null; + + $spaces = str_repeat(' ',$indent); + + //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { + if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); + // It's mapped + if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; } + $string = rtrim ($spaces.$key.': '.$value)."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + private function _doLiteralBlock($value,$indent) { + if ($value === "\n") return '\n'; + if (strpos($value, "\n") === false && strpos($value, "'") === false) { + return sprintf ("'%s'", $value); + } + if (strpos($value, "\n") === false && strpos($value, '"') === false) { + return sprintf ('"%s"', $value); + } + $exploded = explode("\n",$value); + $newValue = '|'; + if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) { + $newValue = $exploded[0]; + unset($exploded[0]); + } + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $line = trim($line); + if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) { + $line = substr($line, 1, -1); + } + $newValue .= "\n" . $spaces . ($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + private function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + + if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } else { + if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY) + $value = '"' . $value . '"'; + if (is_numeric($value) && is_string($value)) + $value = '"' . $value . '"'; + } + + + return $value; + } + + private function isTrueWord($value) { + $words = self::getTranslations(array('true', 'on', 'yes', 'y')); + return in_array($value, $words, true); + } + + private function isFalseWord($value) { + $words = self::getTranslations(array('false', 'off', 'no', 'n')); + return in_array($value, $words, true); + } + + private function isNullWord($value) { + $words = self::getTranslations(array('null', '~')); + return in_array($value, $words, true); + } + + private function isTranslationWord($value) { + return ( + self::isTrueWord($value) || + self::isFalseWord($value) || + self::isNullWord($value) + ); + } + + /** + * Coerce a string into a native type + * Reference: http://yaml.org/type/bool.html + * TODO: Use only words from the YAML spec. + * @access private + * @param $value The value to coerce + */ + private function coerceValue(&$value) { + if (self::isTrueWord($value)) { + $value = true; + } else if (self::isFalseWord($value)) { + $value = false; + } else if (self::isNullWord($value)) { + $value = null; + } + } + + /** + * Given a set of words, perform the appropriate translations on them to + * match the YAML 1.1 specification for type coercing. + * @param $words The words to translate + * @access private + */ + private static function getTranslations(array $words) { + $result = array(); + foreach ($words as $i) { + $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i))); + } + return $result; + } + +// LOADING FUNCTIONS + + private function _load($input) { + $Source = $this->loadFromSource($input); + return $this->loadWithSource($Source); + } + + private function _loadString($input) { + $Source = $this->loadFromString($input); + return $this->loadWithSource($Source); + } + + private function loadWithSource($Source) { + if (empty ($Source)) return array(); + if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { + $array = syck_load (implode ("\n", $Source)); + return is_array($array) ? $array : array(); + } + + $this->path = array(); + $this->result = array(); + + $cnt = count($Source); + for ($i = 0; $i < $cnt; $i++) { + $line = $Source[$i]; + + $this->indent = strlen($line) - strlen(ltrim($line)); + $tempPath = $this->getParentPathByIndent($this->indent); + $line = self::stripIndent($line, $this->indent); + if (self::isComment($line)) continue; + if (self::isEmpty($line)) continue; + $this->path = $tempPath; + + $literalBlockStyle = self::startsLiteralBlock($line); + if ($literalBlockStyle) { + $line = rtrim ($line, $literalBlockStyle . " \n"); + $literalBlock = ''; + $line .= ' '.$this->LiteralPlaceHolder; + $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1])); + while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { + $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent); + } + $i--; + } + + // Strip out comments + if (strpos ($line, '#')) { + $line = preg_replace('/\s*#([^"\']+)$/','',$line); + } + + while (++$i < $cnt && self::greedilyNeedNextLine($line)) { + $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); + } + $i--; + + $lineArray = $this->_parseLine($line); + + if ($literalBlockStyle) + $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); + + $this->addArray($lineArray, $this->indent); + + foreach ($this->delayedPath as $indent => $delayedPath) + $this->path[$indent] = $delayedPath; + + $this->delayedPath = array(); + + } + return $this->result; + } + + private function loadFromSource ($input) { + if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) + $input = file_get_contents($input); + + return $this->loadFromString($input); + } + + private function loadFromString ($input) { + $lines = explode("\n",$input); + foreach ($lines as $k => $_) { + $lines[$k] = rtrim ($_, "\r"); + } + return $lines; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + private function _parseLine($line) { + if (!$line) return array(); + $line = trim($line); + if (!$line) return array(); + + $array = array(); + + $group = $this->nodeContainsGroup($line); + if ($group) { + $this->addGroup($line, $group); + $line = $this->stripGroup ($line, $group); + } + + if ($this->startsMappedSequence($line)) + return $this->returnMappedSequence($line); + + if ($this->startsMappedValue($line)) + return $this->returnMappedValue($line); + + if ($this->isArrayElement($line)) + return $this->returnArrayElement($line); + + if ($this->isPlainArray($line)) + return $this->returnPlainArray($line); + + + return $this->returnKeyValuePair($line); + + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + private function _toType($value) { + if ($value === '') return ""; + $first_character = $value[0]; + $last_character = substr($value, -1, 1); + + $is_quoted = false; + do { + if (!$value) break; + if ($first_character != '"' && $first_character != "'") break; + if ($last_character != '"' && $last_character != "'") break; + $is_quoted = true; + } while (0); + + if ($is_quoted) { + $value = str_replace('\n', "\n", $value); + if ($first_character == "'") + return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\'')); + return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\'')); + } + + if (strpos($value, ' #') !== false && !$is_quoted) + $value = preg_replace('/\s+#(.+)$/','',$value); + + if ($first_character == '[' && $last_character == ']') { + // Take out strings sequences and mappings + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + return $value; + } + + if (strpos($value,': ')!==false && $first_character != '{') { + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + return array($key => $value); + } + + if ($first_character == '{' && $last_character == '}') { + $innerValue = trim(substr ($value, 1, -1)); + if ($innerValue === '') return array(); + // Inline Mapping + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($innerValue); + // Propagate value array + $array = array(); + foreach ($explode as $v) { + $SubArr = $this->_toType($v); + if (empty($SubArr)) continue; + if (is_array ($SubArr)) { + $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; + } + $array[] = $SubArr; + } + return $array; + } + + if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { + return null; + } + + if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){ + $intvalue = (int)$value; + if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX) + $value = $intvalue; + return $value; + } + + if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) { + // Hexadecimal value. + return hexdec($value); + } + + $this->coerceValue($value); + + if (is_numeric($value)) { + if ($value === '0') return 0; + if (rtrim ($value, 0) === $value) + $value = (float)$value; + return $value; + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + private function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + $seqs = array(); + $maps = array(); + $saved_strings = array(); + $saved_empties = array(); + + // Check for empty strings + $regex = '/("")|(\'\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $saved_empties = $strings[0]; + $inline = preg_replace($regex,'YAMLEmpty',$inline); + } + unset($regex); + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $saved_strings = $strings[0]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + // echo $inline; + + $i = 0; + do { + + // Check for sequences + while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { + $seqs[] = $matchseqs[0]; + $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); + } + + // Check for mappings + while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { + $maps[] = $matchmaps[0]; + $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); + } + + if ($i++ >= 10) break; + + } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); + + $explode = explode(',',$inline); + $explode = array_map('trim', $explode); + $stringi = 0; $i = 0; + + while (1) { + + // Re-add the sequences + if (!empty($seqs)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + foreach ($seqs as $seqk => $seq) { + $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); + $value = $explode[$key]; + } + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + foreach ($maps as $mapk => $map) { + $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); + $value = $explode[$key]; + } + } + } + } + + + // Re-add the strings + if (!empty($saved_strings)) { + foreach ($explode as $key => $value) { + while (strpos($value,'YAMLString') !== false) { + $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); + unset($saved_strings[$stringi]); + ++$stringi; + $value = $explode[$key]; + } + } + } + + + // Re-add the empties + if (!empty($saved_empties)) { + foreach ($explode as $key => $value) { + while (strpos($value,'YAMLEmpty') !== false) { + $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1); + $value = $explode[$key]; + } + } + } + + $finished = true; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLMap') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLString') !== false) { + $finished = false; break; + } + if (strpos($value,'YAMLEmpty') !== false) { + $finished = false; break; + } + } + if ($finished) break; + + $i++; + if ($i > 10) + break; // Prevent infinite loops. + } + + + return $explode; + } + + private function literalBlockContinues ($line, $lineIndent) { + if (!trim($line)) return true; + if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; + return false; + } + + private function referenceContentsByAlias ($alias) { + do { + if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } + $groupPath = $this->SavedGroups[$alias]; + $value = $this->result; + foreach ($groupPath as $k) { + $value = $value[$k]; + } + } while (false); + return $value; + } + + private function addArrayInline ($array, $indent) { + $CommonGroupPath = $this->path; + if (empty ($array)) return false; + + foreach ($array as $k => $_) { + $this->addArray(array($k => $_), $indent); + $this->path = $CommonGroupPath; + } + return true; + } + + private function addArray ($incoming_data, $incoming_indent) { + + // print_r ($incoming_data); + + if (count ($incoming_data) > 1) + return $this->addArrayInline ($incoming_data, $incoming_indent); + + $key = key ($incoming_data); + $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; + if ($key === '__!YAMLZero') $key = '0'; + + if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. + if ($key || $key === '' || $key === '0') { + $this->result[$key] = $value; + } else { + $this->result[] = $value; end ($this->result); $key = key ($this->result); + } + $this->path[$incoming_indent] = $key; + return; + } + + + + $history = array(); + // Unfolding inner array tree. + $history[] = $_arr = $this->result; + foreach ($this->path as $k) { + $history[] = $_arr = $_arr[$k]; + } + + if ($this->_containsGroupAlias) { + $value = $this->referenceContentsByAlias($this->_containsGroupAlias); + $this->_containsGroupAlias = false; + } + + + // Adding string or numeric key to the innermost level or $this->arr. + if (is_string($key) && $key == '<<') { + if (!is_array ($_arr)) { $_arr = array (); } + + $_arr = array_merge ($_arr, $value); + } else if ($key || $key === '' || $key === '0') { + if (!is_array ($_arr)) + $_arr = array ($key=>$value); + else + $_arr[$key] = $value; + } else { + if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } + else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } + } + + $reverse_path = array_reverse($this->path); + $reverse_history = array_reverse ($history); + $reverse_history[0] = $_arr; + $cnt = count($reverse_history) - 1; + for ($i = 0; $i < $cnt; $i++) { + $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; + } + $this->result = $reverse_history[$cnt]; + + $this->path[$incoming_indent] = $key; + + if ($this->_containsGroupAnchor) { + $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; + if (is_array ($value)) { + $k = key ($value); + if (!is_int ($k)) { + $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; + } + } + $this->_containsGroupAnchor = false; + } + + } + + private static function startsLiteralBlock ($line) { + $lastChar = substr (trim($line), -1); + if ($lastChar != '>' && $lastChar != '|') return false; + if ($lastChar == '|') return $lastChar; + // HTML tags should not be counted as literal blocks. + if (preg_match ('#<.*?>$#', $line)) return false; + return $lastChar; + } + + private static function greedilyNeedNextLine($line) { + $line = trim ($line); + if (!strlen($line)) return false; + if (substr ($line, -1, 1) == ']') return false; + if ($line[0] == '[') return true; + if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; + return false; + } + + private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) { + $line = self::stripIndent($line, $indent); + if ($literalBlockStyle !== '|') { + $line = self::stripIndent($line); + } + $line = rtrim ($line, "\r\n\t ") . "\n"; + if ($literalBlockStyle == '|') { + return $literalBlock . $line; + } + if (strlen($line) == 0) + return rtrim($literalBlock, ' ') . "\n"; + if ($line == "\n" && $literalBlockStyle == '>') { + return rtrim ($literalBlock, " \t") . "\n"; + } + if ($line != "\n") + $line = trim ($line, "\r\n ") . " "; + return $literalBlock . $line; + } + + function revertLiteralPlaceHolder ($lineArray, $literalBlock) { + foreach ($lineArray as $k => $_) { + if (is_array($_)) + $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); + else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) + $lineArray[$k] = rtrim ($literalBlock, " \r\n"); + } + return $lineArray; + } + + private static function stripIndent ($line, $indent = -1) { + if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); + return substr ($line, $indent); + } + + private function getParentPathByIndent ($indent) { + if ($indent == 0) return array(); + $linePath = $this->path; + do { + end($linePath); $lastIndentInParentPath = key($linePath); + if ($indent <= $lastIndentInParentPath) array_pop ($linePath); + } while ($indent <= $lastIndentInParentPath); + return $linePath; + } + + + private function clearBiggerPathValues ($indent) { + + + if ($indent == 0) $this->path = array(); + if (empty ($this->path)) return true; + + foreach ($this->path as $k => $_) { + if ($k > $indent) unset ($this->path[$k]); + } + + return true; + } + + + private static function isComment ($line) { + if (!$line) return false; + if ($line[0] == '#') return true; + if (trim($line, " \r\n\t") == '---') return true; + return false; + } + + private static function isEmpty ($line) { + return (trim ($line) === ''); + } + + + private function isArrayElement ($line) { + if (!$line || !is_scalar($line)) return false; + if (substr($line, 0, 2) != '- ') return false; + if (strlen ($line) > 3) + if (substr($line,0,3) == '---') return false; + + return true; + } + + private function isHashElement ($line) { + return strpos($line, ':'); + } + + private function isLiteral ($line) { + if ($this->isArrayElement($line)) return false; + if ($this->isHashElement($line)) return false; + return true; + } + + + private static function unquote ($value) { + if (!$value) return $value; + if (!is_string($value)) return $value; + if ($value[0] == '\'') return trim ($value, '\''); + if ($value[0] == '"') return trim ($value, '"'); + return $value; + } + + private function startsMappedSequence ($line) { + return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':'); + } + + private function returnMappedSequence ($line) { + $array = array(); + $key = self::unquote(trim(substr($line,1,-1))); + $array[$key] = array(); + $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); + return array($array); + } + + private function checkKeysInValue($value) { + if (strchr('[{"\'', $value[0]) === false) { + if (strchr($value, ': ') !== false) { + throw new Exception('Too many keys: '.$value); + } + } + } + + private function returnMappedValue ($line) { + $this->checkKeysInValue($line); + $array = array(); + $key = self::unquote (trim(substr($line,0,-1))); + $array[$key] = ''; + return $array; + } + + private function startsMappedValue ($line) { + return (substr ($line, -1, 1) == ':'); + } + + private function isPlainArray ($line) { + return ($line[0] == '[' && substr ($line, -1, 1) == ']'); + } + + private function returnPlainArray ($line) { + return $this->_toType($line); + } + + private function returnKeyValuePair ($line) { + $array = array(); + $key = ''; + if (strpos ($line, ': ')) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(': ', $line); + $key = trim(array_shift($explode)); + $value = trim(implode(': ', $explode)); + $this->checkKeysInValue($value); + } + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if ($key === '0') $key = '__!YAMLZero'; + $array[$key] = $value; + } else { + $array = array ($line); + } + return $array; + + } + + + private function returnArrayElement ($line) { + if (strlen($line) <= 1) return array(array()); // Weird %) + $array = array(); + $value = trim(substr($line,1)); + $value = $this->_toType($value); + if ($this->isArrayElement($value)) { + $value = $this->returnArrayElement($value); + } + $array[] = $value; + return $array; + } + + + private function nodeContainsGroup ($line) { + $symbolsForReference = 'A-z0-9_\-'; + if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) + if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; + if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; + if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; + if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; + return false; + + } + + private function addGroup ($line, $group) { + if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); + if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); + //print_r ($this->path); + } + + private function stripGroup ($line, $group) { + $line = trim(str_replace($group, '', $line)); + return $line; + } +} +} + +// Enable use of Spyc from command line +// The syntax is the following: php Spyc.php spyc.yaml + +do { + if (PHP_SAPI != 'cli') break; + if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; + if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break; + $file = $argv[1]; + echo json_encode (spyc_load_file ($file)); +} while (0); diff --git a/modules/util/Text.class.php b/modules/util/Text.class.php @@ -0,0 +1,393 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002 Jan Dankert, jandankert@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + +/** + * Nuetzliche Funktionen fuer das Bearbeiten von Texten/Zeichenketten + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Text +{ + /** + * + * @param unknown $key + * @param unknown $text + * @return string|unknown + */ + public static function accessKey( $key, $text ) + { + $pos = strpos(strtolower($text),strtolower($key)); + + if ( $pos !== false ) + return substr($text,0,max($pos,0)).'<span class="accesskey">'.substr($text,$pos,1).'</span>'.substr($text,$pos+1); + else + return $text; + } + + /** + * Alias fuer Methode maxLength() + * + * @deprecated use maxlength() ! + */ + public static function maxLaenge( $laenge,$text ) + { + return Text::maxLength($text,$laenge); + } + + + /** + * Einen Text auf eine bestimmte Laenge begrenzen. + * + * Ist der Text zu lang, so wird abgeschnitten und + * eine Zeichenkette angehaengt. + * + * @param String Text, der zu begrenzen ist + * @param Integer maximale Laenge des Textes (optional) + * @param Text, der an gekuerzten Text angehangen wird (optional) + */ + public static function maxLength( $text,$laenge=20,$append='...',$where=STR_PAD_RIGHT ) + { + if ( strlen($text) > $laenge ) + { + if ( $where == STR_PAD_RIGHT ) + $text = substr($text,0,$laenge).$append; + elseif ( $where == STR_PAD_BOTH ) + $text = substr($text,0,$laenge/2).$append.substr($text,strlen($text)-($laenge/2)); + } + + return $text; + } + + + /** + * Umwandeln von BB-Code in Wiki-Textauszeichnungen + * + * @param text zu bearbeitender Text + * + * @return String Ausgabe + */ + public static function bbCode2Wiki( $inhalt ) + { + $inhalt = preg_replace('/\[b\]([^\[]*)\[\/b\]/i' , '*\\1*' ,$inhalt); + $inhalt = preg_replace('/\[i\]([^\[]*)\[\/i\]/i' , '_\\1_' ,$inhalt); + $inhalt = preg_replace('/\[code\]([^\[]*)\[\/code\]/i' , '=\\1=' ,$inhalt); + + $inhalt = preg_replace('/\[url\]([^\[]*)\[\/url\]/i' ,'"\\1"->"\\1"' ,$inhalt); + $inhalt = preg_replace('/\[url=([^\[]*)\]([^\[]*)\[\/url\]/i' ,'"\\2"->"\\1"' ,$inhalt); + + return $inhalt; + } + + + /** + * Umwandeln von einfachen HTML-Befehlen in Wiki-Textauszeichnungen + * + * @param text zu bearbeitender Text + * + * @return String Ausgabe + */ + public static function Html2Wiki( $inhalt ) + { + $inhalt = preg_replace('/<b(.*)>(.*)<\/b>/i','*\\2*' ,$inhalt); + $inhalt = preg_replace('/<i(.*)>(.*)<\/i>/i','_\\2_' ,$inhalt); + $inhalt = preg_replace('/<a(.*)href="(.*)">(.*)<\/a>/i','"\\3"->"\\2"' ,$inhalt); + + return $inhalt; + } + + + /** + * HTML-Entitaeten fuer HTML-Tags verwenden + * + * @param String Text, in dem HTML-Tags umgewandelt werden sollen + * @return String Ausgabe + */ + public static function encodeHtml( $inhalt ) + { + //$inhalt = str_replace('&','&amp;',$inhalt); + $inhalt = str_replace('"','&quot;',$inhalt); + $inhalt = str_replace('<','&lt;' ,$inhalt); + $inhalt = str_replace('>','&gt;' ,$inhalt); + + return $inhalt; + } + + + + /** + * Ersetzt Sonderzeichen durch HTML-�quivalente.<br> + * Z.B. Ersetzt "(c)" durch "&copy;". + */ + public static function replaceHtmlChars( $text ) + { + global $conf; + + foreach( explode(' ',$conf['editor']['html']['replace']) as $repl ) + { + list( $ersetze, $mit ) = explode(':',$repl.':'); + $text = str_replace($ersetze, $mit, $text); + } + + return $text; + } + + + + /** + * HTML-Entitaeten fuer HTML-Tags verwenden + * + * @param String Text, in dem HTML-Tags umgewandelt werden sollen + * @return String Ausgabe + */ + public static function encodeHtmlSpecialChars( $inhalt ) + { + return Text::replaceHtmlChars( $inhalt ); + } + + + + /** + * Vergleicht 2 Text-Arrays und ermittelt eine Darstellung der Unterschiede + * + */ + public static function diff( $from_text,$to_text ) + { + // Zaehler pro Textarray + $pos_from = -1; + $pos_to = -1; + + // Ergebnis-Arrays + $from_out = array(); + $to_out = array(); + + while( true ) + { + $pos_from++; + $pos_to ++; + + if ( !isset($from_text[$pos_from]) && + !isset($to_text [$pos_to ]) ) + { + // Text in ist 'neu' und 'alt' zuende. Ende der Schleife. + break; + } + elseif + ( isset($from_text[$pos_from]) && + !isset($to_text [$pos_to]) ) + { + // Text in 'neu' ist zuende, die Restzeilen von 'alt' werden ausgegeben + while( isset($from_text[$pos_from]) ) + { + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'old'); + $to_out [] = array(); + $pos_from++; + } + break; + } + elseif + ( !isset($from_text[$pos_from]) && + isset($to_text [$pos_to]) ) + { + // Umgekehrter Fall: Text in 'alt' ist zuende, Restzeilen aus 'neu' werden ausgegeben + while( isset($to_text[$pos_to]) ) + { + $from_out[] = array(); + $to_out [] = array('text'=>$to_text[$pos_to],'line'=>$pos_to+1,'type'=>'new'); + $pos_to++; + } + break; + } + elseif + ( rtrim($from_text[$pos_from]) != rtrim($to_text[$pos_to]) ) + { + // Zeilen sind vorhanden, aber ungleich + // Wir suchen jetzt die naechsten beiden Zeilen, die gleich sind. + $max_entf = min(count($from_text)-$pos_from-1,count($to_text)-$pos_to-1); + + #echo "suche start, max_entf=$max_entf, pos_from=$pos_from, pos_to=$pos_to<br/>"; + + for ( $a=0; $a<=$max_entf; $a++ ) + { + #echo "a ist $a<br/>"; + for ( $b=0; $b<=$max_entf; $b++ ) + { + #echo "b ist $b<br/>"; + if ( trim($from_text[$pos_from+$b]) != '' && + $from_text[$pos_from+$b] == $to_text[$pos_to+$a] ) + { + $pos_gef_from = $pos_from+$b; + $pos_gef_to = $pos_to +$a; + break; + } + + if ( trim($from_text[$pos_from+$a]) != '' && + $from_text[$pos_from+$a] == $to_text[$pos_to+$b] ) + { + $pos_gef_from = $pos_from+$a; + $pos_gef_to = $pos_to +$b; + break; + } + } + + if ( $b <=$max_entf) + { + break; + } + } + + if ( $a<=$max_entf ) + { + // Gleiche Zeile gefunden + #echo "gefunden, pos_gef_from=$pos_gef_from, pos_gef_to=$pos_gef_to<br/>"; + + if ( $pos_gef_from - $pos_from == 0 ) + $type = 'new'; + elseif + ( $pos_gef_to - $pos_to == 0 ) + $type = 'old'; + else + $type = 'notequal'; + + while( $pos_gef_from - $pos_from > 0 && + $pos_gef_to - $pos_to > 0 ) + { + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type); + $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type); + + $pos_from++; + $pos_to++; + } + + while( $pos_gef_from - $pos_from > 0 ) + { + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type); + $to_out [] = array(); + $pos_from++; + } + + while( $pos_gef_to - $pos_to > 0 ) + { + $from_out[] = array(); + $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type); + $pos_to++; + } + $pos_from--; + $pos_to--; + } + else + { + // Keine gleichen Zeilen gefunden + #echo "nicht gefunden, i=$i, j=$j, pos_from war $pos_from, pos_to war $pos_to<br/>"; + + while( true ) + { + if ( !isset($from_text[$pos_from]) && + !isset($to_text [$pos_to ]) ) + { + break; + } + elseif + ( isset($from_text[$pos_from]) && + !isset($to_text [$pos_to ]) ) + { + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal'); + $to_out [] = array(); + } + elseif + ( !isset($from_text[$pos_from]) && + isset($to_text [$pos_to ]) ) + { + $from_out[] = array(); + $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal'); + } + else + { + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal'); + $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal'); + } + $pos_from++; + $pos_to++; + } + } + } + else + { + // Zeilen sind gleich + $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'equal'); + $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'equal'); + } + } + + return( array($from_out,$to_out) ); + } + + + /** + * Entfernt einen Text-Bereich aus einer Zeichenkette.<br> + * Es wird angegeben, von wo bis wo entfernt werden soll. + * + * @param $text Text, aus dem entfernt wird + * @param $von der Text, AB dem entfernt wird + * @param $bis der Text, BIS ZU DEM entfernt wird + * @return String Text + */ + public static function entferneVonBis($text,$von,$bis) + { + $beg = strpos($text,$von); + $end = strpos($text,$bis); + if ( $beg!==false && $end !==false ) + $text = substr($text,0,$beg).substr($text,$end+strlen($bis)); + return $text; + } + + + /** + * Saeubert eine Zeichenkette. + * + * Es werden ungueltige Zeichen aus einer Zeichenkette entfernt. Es wird mit einer Whitelist + * gearbeitet, d.h. die erlaubten Zeichen werden angegeben. + * + * @param $eingabe Die Eingabe-Zeichenkette, aus der ungueltige Zeichen entfernt werden sollen. + * @param $erlaubt Die erlaubten Zeichen (eine "White-List") + * @return String die aufgeräumte Zeichenkette + */ + public static function clean( $eingabe, $erlaubt ) + { + $first = strtr( $eingabe, $erlaubt, str_repeat("\x01", strlen($erlaubt)) ); + $second = strtr( $eingabe, $first , str_repeat("\x00", strlen($first )) ); + return str_replace("\x00",'',$second); + } + + + + public static function parseOID( $text ) + { + $oids = array(); + $treffer = array(); + + preg_match_all('/\"([^\"]*)__OID__([0-9]+)__([^\"]*)\"/', $text, $treffer,PREG_SET_ORDER); + + foreach( $treffer as $t ) + $oids[$t[2]] = $t[0]; + + return $oids; + } +} + diff --git a/modules/util/Transformer.class.php b/modules/util/Transformer.class.php @@ -0,0 +1,102 @@ +<?php + +/** + * Transformieren eines Textes.<br> + * Ein Text wird geparst und neu gerendert. + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Transformer +{ + var $text = ''; + var $doc; + var $page; + var $element; + + function transform() + { + $this->parseDocument(); + $this->renderDocument(); + + $this->text = $this->renderedText; + } + + + + /** + * Parsen eines Textes.<br> + * Der Text muss in der Eigenschaft 'text' bereits zur Verf�gung stehen.<br> + * Der Text wird geparst und als DOM (Document object model) intern gespeichert. + */ + + function parseDocument() + { + // Den Text zeilenweise aufteilen. + $zeilen = explode("\n",$this->text); + + // Dokument erzeugen und den Text parsen. + $parser = new WikiParser(); + $this->doc = new DocumentElement(); + $this->doc->element = $this->element; + $this->doc->parse( $zeilen ); + $this->doc->page = $this->page; + } + + + + /** + * Das interne Dokumente wird gerendet.<br> + * Die fertige Ausgabe steht anschliessend in der Eigenschaft "renderedText" zur Verf�gung. + */ + function renderDocument() + { + $this->doc->encodeHtml = !$this->element->html; + + $text = $this->doc->render( $this->page->mimeType() ); + + // Liste der verlinkten Objekt-Ids. + // Die Objekt-Ids werden absteigend sortiert, damit z.B. '33' vor '3' ersetzt wird. + $linkedObjectIds = $this->doc->linkedObjectIds; + rsort( $linkedObjectIds,SORT_NUMERIC ); + + // Links object:nnn ersetzen + // + // Das Dokument-Objekt hat keine Information ueber die aktuelle Seite, + // daher werden die Links auf Objekte hier gesetzt. + foreach( $linkedObjectIds as $objectId ) + { + $targetPath = $this->page->path_to_object( $objectId ); + + // Hack: Sonderzeichen muessen in URLs maskiert werden, aber nur bei URLs die aus Link-Objekten kommen, bei allem + // anderen (insbesondere Preview-Links zu andereen Seiten) darf die Umsetzung nicht erfolgen. + // Der Renderer kann dies nicht tun, denn der erzeugt nur "object://..."-URLs. + // Beispiel: "...?a=1&b=2" wird zu "...?a=1&amp;b=2" + $o = new Object($objectId); + try + { + $o->load(); + if ( $o->isLink ) + { + $l = new Link($objectId); + $l->load(); + if ( $l->isLinkToUrl && $this->page->mimeType() == 'text/html' ) + $targetPath = htmlspecialchars($targetPath); + } + } + catch( ObjectNotFoundException $e) + { + $targetPath = 'javascript:alert("object '.$objectId.' not found");'; + } + + + $text = str_replace( 'object:' .$objectId, $targetPath, $text ); + $text = str_replace( 'object://'.$objectId, $targetPath, $text ); + } + + $this->renderedText = $text; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/TreeElement.class.php b/modules/util/TreeElement.class.php @@ -0,0 +1,75 @@ +<?php + +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Darstellen eines Elementes in einer Baumstruktur + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class TreeElement +{ + /** + * @type Integer + */ + var $id; + + var $extraId = array(); + + var $internalId = 0; + + /** + * Text des Baumelementes + * @type String + */ + var $text = ""; + + /** + * Beschreibung + * @type String + */ + var $description = ""; + var $url = ""; + var $icon = ""; + var $target = ""; + var $action = ""; + + /** + * Unterelemente + * Ein Array von Ids + * @type Array + */ + var $subElementIds = array(); + + /** + * Typ des Elementes + * In der Tree-Klasse muss es eine Methode mit diesem Namen geben, die das + * Element laedt. + * @type String + */ + var $type = ""; + + + // Konstruktor + function __construct() + { + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/Upload.class.php b/modules/util/Upload.class.php @@ -0,0 +1,85 @@ +<?php +// OpenRat Content Management System +// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** + * Methoden fuer den Upload einer Datei + * + * @author $Author$ + * @version $Revision$ + * @package openrat.services + */ +class Upload +{ + var $filename; + var $extension; + var $value; + var $size; + var $error = ''; + + + /** + * Stellt fest, ob der Upload geklappt hat. + * + * @return boolean + */ + function isValid() + { + return empty($this->error); + } + + + + /** + * Bearbeitet den Upload einer Datei.<br> + * Bei der Objekterzeugung wird die Datei bereits geladen.<br> + * + * @return Upload + */ + function __construct( $name='file' ) // Konstruktor + { + global $FILES; + + if ( !isset($FILES[$name]) || + !isset($FILES[$name]['tmp_name']) || + !is_file($FILES[$name]['tmp_name']) ) + { + $this->error = 'No file received.'; + return; + } + + $this->size = filesize($FILES[$name]['tmp_name']); + + $fh = fopen( $FILES[$name]['tmp_name'],'r' ); + + $this->value = fread($fh,$this->size); + fclose( $fh ); + + $this->filename = $FILES[$name]['name']; + $this->extension = ''; + + $p = strrpos( $this->filename,'.' ); // Letzten Punkt suchen + + if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen + { + $this->extension = substr( $this->filename,$p+1 ); + $this->filename = substr( $this->filename,0,$p ); + } + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/XML.class.php b/modules/util/XML.class.php @@ -0,0 +1,169 @@ +<?php +/** + * Multidimensional Array-to-XML. + * + * Example: + * $xml = new XML(); + * header('Content-Type: application/xml'); + * echo $xml->encode( $yourBigArray ); + * exit; + * + * Author: Honor� Vasconcelos, Jan Dankert + * + * Original from: + * Clean XML To Array: http://www.phpclasses.org/browse/package/3598.html + * + * License of this class: BSD-Licence. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this + * list of conditions and the following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * Neither the name of the Author(s) nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +class XML +{ + /** + * Parse multidimentional array to XML. + * + * @param array $array + * @return String + */ + var $xmlText = ''; + + + /** + * Name of the root element. + * + * @var String + */ + var $root = 'xml'; + + /* + * Char to indent with. + * + * @var String + */ + var $indentChar = "\t"; + + + /** + * Newline-Char + * @var String + */ + var $nl = "\n"; + + /** + * Encode a array to XML. + * + * @param Array $array + * @return String (serialized XML) + */ + function encode($array) + { + //star and end the XML document + $this->xmlText = '<?xml version="1.0" encoding="utf-8"?>'.$this->nl; + $this->xmlText .= '<'.$this->root.'>'.$this->nl; + $this->array_transform($array,1); + $this->xmlText .='</'.$this->root.'>'; + + return $this->xmlText; + } + + + /** + * @access private + */ + function array_transform($array,$depth){ + + foreach($array as $key => $value) + { + $attr = array(); + if ( is_numeric($key) ) + { + // Array-Einträge mit numerischen Index können nicht direkt in ein XML-Element + // umgewandelt werden, da nur-numerische Element-Namen nicht erlaubt sind. + // Daher verwenden wir dann 'entry' als Elementnamen. + $attr['id'] = $key; + $key = 'entry'; + } + + $indent = str_repeat($this->indentChar,$depth); + + if ( empty($value) ) + { + $this->xmlText .= $indent.$this->shortTag($key,$attr).$this->nl; + } + elseif ( is_object($value) ) + { + // Der Inhalt ist ein Array, daher rekursiv verzweigen. + $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl; + $prop = get_object_vars($value); + $this->array_transform($prop,$depth+1); // Rekursiver Aufruf + $this->xmlText .= $indent.$this->closeTag($key).$this->nl; + } + elseif ( is_array($value) ) + { + // Der Inhalt ist ein Array, daher rekursiv verzweigen. + $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl; + $this->array_transform($value,$depth+1); // Rekursiver Aufruf + $this->xmlText .= $indent.$this->closeTag($key).$this->nl; + } + else + { + // Der Inhalt ist ein einfacher Inhalt (kein Array). + $this->xmlText .= $indent.$this->openTag($key,$attr); + $this->xmlText .= $value; + $this->xmlText .= $this->closeTag($key).$this->nl; + } + } + } + + + function openTag($key,$attr) + { + $tag = '<'.$key; + foreach( $attr as $attr_name=>$attr_value ) + $tag .= ' '.$attr_name.'="'.$attr_value.'"'; + $tag .= '>'; + return $tag; + } + + + + function shortTag($key,$attr) + { + $tag = '<'.$key; + foreach( $attr as $attr_name=>$attr_value ) + $tag .= ' '.$attr_name.'="'.$attr_value.'"'; + $tag .= ' />'; + return $tag; + } + + + + function closeTag($key) + { + return '</'.$key.'>'; + } +} + +?>+ \ No newline at end of file diff --git a/modules/util/config-default.php b/modules/util/config-default.php @@ -0,0 +1,880 @@ +<?php +// DO NOT MAKE ANY CHANGES IN THIS FILE, please edit the file 'config.yml' or 'config-<host>.yml' instead. +// This file should only be changed by developers. +$conf = array(); +$conf['applications'] = array(); +$conf['applications']['']=0; +$conf['applications']['phpmyadmin'] = array(); +$conf['applications']['phpmyadmin']['name']='PHPYourAdmin'; +$conf['applications']['phpmyadmin']['url']="https://example.com/anotherapplication/index.cgi"; +$conf['applications']['phpmyadmin']['param']="ticketidforopenrat"; +$conf['applications']['phpmyadmin']['group']='0'; +$conf['applications']['phpmyadmin']['description']="Your database administration"; +$conf['cache'] = array(); +$conf['cache']['conditional_get']=true; +$conf['cache']['enable_cache']=false; +$conf['cache']['tmp_dir']=""; +$conf['config'] = array(); +$conf['config']['auto_reload']= true; +$conf['config']['session_destroy_on_config_reload']= true; +$conf['content'] = array(); +$conf['content']['file'] = array(); +$conf['content']['file']['max_file_size']='1500'; +$conf['content']['revision-limit'] = array(); +$conf['content']['revision-limit']['enabled']= false; +$conf['content']['revision-limit']['max-age']= 120; +$conf['content']['revision-limit']['min-age']= 1; +$conf['content']['revision-limit']['max-revisions']= 100; +$conf['content']['revision-limit']['min-revisions']= 3; +$conf['content']['language'] = array(); +$conf['content']['language']['use_default_language']= true; +$conf['countries'] = array(); +$conf['countries']['']='0'; +$conf['countries']['AA']='Afar'; +$conf['countries']['AB']='Abkhazian'; +$conf['countries']['AF']='Afrikaans'; +$conf['countries']['AM']='Amharic'; +$conf['countries']['AR']='Arabic'; +$conf['countries']['AS']='Assamese'; +$conf['countries']['AY']='Aymara'; +$conf['countries']['AZ']='Azerbaijani'; +$conf['countries']['BA']='Bashkir'; +$conf['countries']['BE']='Byelorussian'; +$conf['countries']['BG']='Bulgarian'; +$conf['countries']['BH']='Bihari'; +$conf['countries']['BI']='Bislama'; +$conf['countries']['BN']='Bengali'; +$conf['countries']['BO']='Tibetan'; +$conf['countries']['BR']='Breton'; +$conf['countries']['CA']='Catalan'; +$conf['countries']['CO']='Corsican'; +$conf['countries']['CS']='Czech'; +$conf['countries']['CY']='Welsh'; +$conf['countries']['DA']='Danish'; +$conf['countries']['DE']='German'; +$conf['countries']['DZ']='Bhutani'; +$conf['countries']['EL']='Greek'; +$conf['countries']['EN']='English'; +$conf['countries']['EO']='Esperanto'; +$conf['countries']['ES']='Spanish'; +$conf['countries']['ET']='Estonian'; +$conf['countries']['EU']='Basque'; +$conf['countries']['FA']='Persian'; +$conf['countries']['FI']='Finnish'; +$conf['countries']['FJ']='Fiji'; +$conf['countries']['FO']='Faeroese'; +$conf['countries']['FR']='French'; +$conf['countries']['FY']='Frisian'; +$conf['countries']['GA']='Irish'; +$conf['countries']['GD']='Gaelic'; +$conf['countries']['GL']='Galician'; +$conf['countries']['GN']='Guarani'; +$conf['countries']['GU']='Gujarati'; +$conf['countries']['HA']='Hausa'; +$conf['countries']['HI']='Hindi'; +$conf['countries']['HR']='Croatian'; +$conf['countries']['HU']='Hungarian'; +$conf['countries']['HY']='Armenian'; +$conf['countries']['IA']='Interlingua'; +$conf['countries']['IE']='Interlingue'; +$conf['countries']['IK']='Inupiak'; +$conf['countries']['IN']='Indonesian'; +$conf['countries']['IS']='Icelandic'; +$conf['countries']['IT']='Italian'; +$conf['countries']['IW']='Hebrew'; +$conf['countries']['JA']='Japanese'; +$conf['countries']['JI']='Yiddish'; +$conf['countries']['JW']='Javanese'; +$conf['countries']['KA']='Georgian'; +$conf['countries']['KK']='Kazakh'; +$conf['countries']['KL']='Greenlandic'; +$conf['countries']['KM']='Cambodian'; +$conf['countries']['KN']='Kannada'; +$conf['countries']['KO']='Korean'; +$conf['countries']['KS']='Kashmiri'; +$conf['countries']['KU']='Kurdish'; +$conf['countries']['KY']='Kirghiz'; +$conf['countries']['LA']='Latin'; +$conf['countries']['LN']='Lingala'; +$conf['countries']['LO']='Laothian'; +$conf['countries']['LT']='Lithuanian'; +$conf['countries']['LV']='Latvian'; +$conf['countries']['MG']='Malagasy'; +$conf['countries']['MI']='Maori'; +$conf['countries']['MK']='Macedonian'; +$conf['countries']['ML']='Malayalam'; +$conf['countries']['MN']='Mongolian'; +$conf['countries']['MO']='Moldavian'; +$conf['countries']['MR']='Marathi'; +$conf['countries']['MS']='Malay'; +$conf['countries']['MT']='Maltese'; +$conf['countries']['MY']='Burmese'; +$conf['countries']['NA']='Nauru'; +$conf['countries']['NE']='Nepali'; +$conf['countries']['NL']='Dutch'; +$conf['countries']['_NO']='Norwegian'; +$conf['countries']['OC']='Occitan'; +$conf['countries']['OM']='Oromo'; +$conf['countries']['OR']='Oriya'; +$conf['countries']['PA']='Punjabi'; +$conf['countries']['PL']='Polish'; +$conf['countries']['PS']='Pashto'; +$conf['countries']['PT']='Portuguese'; +$conf['countries']['QU']='Quechua'; +$conf['countries']['RM']='Rhaeto-Romance'; +$conf['countries']['RN']='Kirundi'; +$conf['countries']['RO']='Romanian'; +$conf['countries']['RU']='Russian'; +$conf['countries']['RW']='Kinyarwanda'; +$conf['countries']['SA']='Sanskrit'; +$conf['countries']['SD']='Sindhi'; +$conf['countries']['SG']='Sangro'; +$conf['countries']['SH']='Serbo-Croatian'; +$conf['countries']['SI']='Singhalese'; +$conf['countries']['SK']='Slovak'; +$conf['countries']['SL']='Slovenian'; +$conf['countries']['SM']='Samoan'; +$conf['countries']['SN']='Shona'; +$conf['countries']['SO']='Somali'; +$conf['countries']['SQ']='Albanian'; +$conf['countries']['SR']='Serbian'; +$conf['countries']['SS']='Siswati'; +$conf['countries']['ST']='Sesotho'; +$conf['countries']['SU']='Sudanese'; +$conf['countries']['SV']='Swedish'; +$conf['countries']['SW']='Swahili'; +$conf['countries']['TA']='Tamil'; +$conf['countries']['TE']='Tegulu'; +$conf['countries']['TG']='Tajik'; +$conf['countries']['TH']='Thai'; +$conf['countries']['TI']='Tigrinya'; +$conf['countries']['TK']='Turkmen'; +$conf['countries']['TL']='Tagalog'; +$conf['countries']['TN']='Setswana'; +$conf['countries']['TO']='Tonga'; +$conf['countries']['TR']='Turkish'; +$conf['countries']['TS']='Tsonga'; +$conf['countries']['TT']='Tatar'; +$conf['countries']['TW']='Twi'; +$conf['countries']['UK']='Ukrainian'; +$conf['countries']['UR']='Urdu'; +$conf['countries']['UZ']='Uzbek'; +$conf['countries']['VI']='Vietnamese'; +$conf['countries']['VO']='Volapuk'; +$conf['countries']['WO']='Wolof'; +$conf['countries']['XH']='Xhosa'; +$conf['countries']['YO']='Yoruba'; +$conf['countries']['ZH']='Chinese'; + +$conf['database'] = array(); + +$conf['database-default']=array(); +$conf['database-default']['defaults']=array(); +$conf['database-default']['defaults']['prefix' ] = ''; +$conf['database-default']['defaults']['suffix' ] = ''; +$conf['database-default']['defaults']['enabled' ] = true; +$conf['database-default']['defaults']['name' ] = ''; +$conf['database-default']['defaults']['description'] = ''; +$conf['database-default']['defaults']['type' ] = 'pdo'; +$conf['database-default']['defaults']['dsn' ] = 'mysql:localhost'; +$conf['database-default']['defaults']['user' ] = ''; +$conf['database-default']['defaults']['password' ] = ''; +$conf['database-default']['defaults']['host' ] = ''; +$conf['database-default']['defaults']['database' ] = ''; +$conf['database-default']['defaults']['base64' ] = false; +$conf['database-default']['defaults']['persistent' ] = true; +$conf['database-default']['defaults']['charset' ] = 'UTF-8'; +$conf['database-default']['defaults']['connection_sql'] = ''; +$conf['database-default']['defaults']['cmd' ] = ''; +$conf['database-default']['defaults']['prepare' ] = true; +$conf['database-default']['defaults']['transaction'] = true; +$conf['database-default']['defaults']['update' ] = array(); +$conf['database-default']['defaults']['auto_update'] = true; +$conf['date'] = array(); +$conf['date']['format'] = array(); +$conf['date']['format']['SHORT']= ""; +$conf['date']['format']['ISO8601SHORT']= "Ymd"; +$conf['date']['format']['ISO8601']= "Y-m-d"; +$conf['date']['format']['ISO8601BAS']= "YmdTHis"; +$conf['date']['format']['ISO8601EXT']= "Y-m-dTH:i:s"; +$conf['date']['format']['ISO8601FULL']= "Y-m-dTH:i:sO"; +$conf['date']['format']['ISO8601WEEK']= "YWW"; +$conf['date']['format']['GER1']= "d.m.Y"; +$conf['date']['format']['GER2']= "d.m.Y, H:i"; +$conf['date']['format']['GER3']= "d.m.Y, H:i:s"; +$conf['date']['format']['GER4']= "d. F Y, H:i:s"; +$conf['date']['format']['ENGLONG']= "l dS of F Y h:i:s A"; +$conf['date']['format']['GMDATE']= "D, d M Y H:i:s GMT"; +$conf['date']['format']['RFC822']= "r"; +$conf['date']['format']['UNIX']= "U"; +$conf['date']['format']['LONG']= "F j, Y, g:i a"; +$conf['date']['timezone'] = array(); +$conf['date']['timezone']['-6']="New York"; +$conf['date']['timezone']['0']="UTC (GMT)"; +$conf['date']['timezone']['60']="MET (Middle European Time)"; +$conf['date']['timezone']['120']="MEST (Middle European Summertime)"; +$conf['editor'] = array(); +$conf['editor']['text-markup'] = array(); +$conf['editor']['text-markup']['strong-begin']= "*"; +$conf['editor']['text-markup']['strong-end']= "*"; +$conf['editor']['text-markup']['emphatic-begin']= "_"; +$conf['editor']['text-markup']['emphatic-end']= "_"; +$conf['editor']['text-markup']['image-begin']= "{"; +$conf['editor']['text-markup']['image-end']= "}"; +$conf['editor']['text-markup']['speech-begin']='QUOTE'; +$conf['editor']['text-markup']['speech-end']='QUOTE'; +$conf['editor']['text-markup']['code-begin']= "="; +$conf['editor']['text-markup']['code-end']= "="; +$conf['editor']['text-markup']['footnote-begin']= "["; +$conf['editor']['text-markup']['footnote-end']= "]"; +$conf['editor']['text-markup']['pre-begin']= "="; +$conf['editor']['text-markup']['pre-end']= "="; +$conf['editor']['text-markup']['insert-begin']= "++"; +$conf['editor']['text-markup']['insert-end']= "++"; +$conf['editor']['text-markup']['remove-begin']= "--"; +$conf['editor']['text-markup']['remove-end']= "--"; +$conf['editor']['text-markup']['definition-sep']= "::"; +$conf['editor']['text-markup']['headline']= "+"; +$conf['editor']['text-markup']['headline_level1_underline']= "="; +$conf['editor']['text-markup']['headline_level2_underline']= "-"; +$conf['editor']['text-markup']['headline_level3_underline']= "."; +$conf['editor']['text-markup']['list-unnumbered']= "-"; +$conf['editor']['text-markup']['list-numbered']= "#"; +$conf['editor']['text-markup']['table-of-content']= "##TOC##"; +$conf['editor']['text-markup']['linkto']= "->"; +$conf['editor']['text-markup']['table-cell-sep']= "|"; +$conf['editor']['text-markup']['style-begin']= "'"; +$conf['editor']['text-markup']['style-end']= "'"; +$conf['editor']['text-markup']['quote']= ">"; +$conf['editor']['text-markup']['quote-line-begin']= ">"; +$conf['editor']['text-markup']['quote-line-end']= ">"; +$conf['editor']['text-markup']['macro-begin']= "<<"; +$conf['editor']['text-markup']['macro-end']= ">>"; +$conf['editor']['text-markup']['macro-attribute-quote']= "'"; +$conf['editor']['text-markup']['macro-attribute-value-seperator']= "="; +$conf['editor']['html'] = array(); +$conf['editor']['html']['tag_strong']= "strong"; +$conf['editor']['html']['tag_emphatic']= "em"; +$conf['editor']['html']['tag_teletype']= "tt"; +$conf['editor']['html']['tag_speech']= "cite"; +$conf['editor']['html']['override_speech']=false; +$conf['editor']['html']['override_speech_open']= "&laquo;"; +$conf['editor']['html']['override_speech_close']= "&raquo;"; +$conf['editor']['html']['rendermode']="sgml"; +$conf['editor']['html']['rendermode']="xml"; +$conf['editor']['html']['replace']= "EUR:&euro;"; +$conf['editor']['wiki'] = array(); +$conf['editor']['wiki']['convert_html']=true; +$conf['editor']['wiki']['convert_bbcode']=true; +$conf['editor']['text'] = array(); +$conf['editor']['text']['linelength']='70'; +$conf['editor']['calendar'] = array(); +$conf['editor']['calendar']['weekday_offset']='1'; +$conf['editor']['text'] = array(); +$conf['editor']['text']['linelength']='70'; +$conf['editor']['macro'] = array(); +$conf['editor']['macro']['show_errors']=false; +$conf['filename'] = array(); +$conf['filename']['edit']=true; +$conf['filename']['default']='index'; +$conf['filename']['style']='short'; +$conf['filename']['url']='relative'; +$conf['ftp'] = array(); +$conf['ftp']['ascii']= "html,htm,php"; +$conf['help'] = array(); +$conf['help']['enabled']=true; +$conf['help']['url']="http://help.openrat.de/"; +$conf['help']['suffix']=".html"; +$conf['html'] = array(); +$conf['html']['tag_teletype']='tt'; +$conf['html']['tag_emphatic']='em'; +$conf['html']['tag_strong']='strong'; +$conf['html']['tag_speech']='cite'; +$conf['html']['speech_open']= "&bdquo"; +$conf['html']['speech_close']= "&rdquo"; +$conf['i18n'] = array(); +$conf['i18n']['use_http']=true; +$conf['i18n']['default']='de'; +$conf['i18n']['available']='de,en,es,fr,it,ru,cn'; +$conf['i18n']['locale'] = array(); +$conf['i18n']['locale']['de']="de_DE.utf8"; +$conf['i18n']['locale']['en']="en_US.utf8"; +$conf['image'] = array(); +$conf['image']['truecolor']=true; +$conf['interface'] = array(); +$conf['interface']['tree_width']= "25%"; +$conf['interface']['file_separator']= " &raquo"; +$conf['interface']['nice_urls']=false; +$conf['interface']['url_sessionid']=false; +$conf['interface']['theme']= 'default'; +$conf['interface']['timeout']='0'; +$conf['interface']['override_title']=''; +$conf['interface']['style'] = array(); +$conf['interface']['style']['default']='modern'; +$conf['interface']['config'] = array(); +$conf['interface']['config']['file_manager_url']=""; +$conf['interface']['config']['enable']=true; +$conf['interface']['config']['show_system']=true; +$conf['interface']['config']['show_interpreter']=true; +$conf['interface']['config']['show_extensions']=true; +$conf['interface']['frames'] = array(); +$conf['interface']['frames']['top']='_top'; +$conf['interface']['url'] = array(); +$conf['interface']['url']['fake_url']=false; +$conf['interface']['url']['index']=false; +$conf['interface']['url']['url_format']= "%s,%s.%i"; +$conf['interface']['url']['url_format']= "%s,%s,%d.do"; +$conf['interface']['url']['add_sessionid']=false; +$conf['interface']['gravatar'] = array(); +$conf['interface']['gravatar']['enable']=true; +$conf['interface']['gravatar']['size']='80'; +$conf['interface']['gravatar']['default']='404'; +$conf['interface']['gravatar']['rating']='g'; +$conf['interface']['session'] = array(); +$conf['interface']['session']['auto_extend']=true; +$conf['ldap'] = array(); +$conf['ldap']['host']="localhost"; +$conf['ldap']['port']="389"; +$conf['ldap']['protocol']="2"; +$conf['ldap']['dn']= "uid={user},ou=users,dc=example,dc=com"; +$conf['ldap']['dn']= ""; +$conf['ldap']['search'] = array(); +$conf['ldap']['search']['anonymous']=true; +$conf['ldap']['search']['user']= "uid=openrat,ou=users,dc=example,dc=com"; +$conf['ldap']['search']['password']= "verysecret"; +$conf['ldap']['search']['basedn']= "dc=example,dc=com"; +$conf['ldap']['search']['filter']= "(uid={user})"; +$conf['ldap']['search']['aliases']=true; +$conf['ldap']['search']['timeout']= 30; +$conf['ldap']['search']['add']=true; +$conf['ldap']['authorize'] = array(); +$conf['ldap']['authorize']['group_filter']="(memberUid={dn})"; +$conf['ldap']['authorize']['group_name']="cn"; +$conf['ldap']['authorize']['auto_add']=true; +$conf['login'] = array(); +$conf['login']['motd']=''; +$conf['login']['nologin']=false; +$conf['login']['register']=false; +$conf['login']['send_password']=false; +$conf['login']['gpl'] = array(); +$conf['login']['gpl']['url']="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"; +$conf['login']['logo'] = array(); +$conf['login']['logo']['enabled']=false; +$conf['login']['logo']['image']="./themes/default/images/logo.jpg" ; +$conf['login']['logo']['url']="http://www.openrat.de" ; +$conf['login']['start'] = array(); +$conf['login']['start']['start_lastchanged_object']=true; +$conf['login']['start']['start_single_project']=true; +$conf['login']['default-database']='db'; +$conf['log'] = array(); +$conf['log']['file']= null; +$conf['log']['level']= "warn"; +$conf['log']['date_format']= "M j H:i:s"; +$conf['log']['ns_lookup']=false; +$conf['log']['format']= "%time %level %host %user %action %text"; +$conf['mail'] = array(); +$conf['mail']['enabled']=true; +$conf['mail']['from']="OpenRat <user@example.com>"; +$conf['mail']['signature']="http://www.openrat.de"; +$conf['mail']['cc']='0'; +$conf['mail']['bcc']='0'; +$conf['mail']['priority']='3'; +$conf['mail']['header_encoding']="Quoted-printable"; +$conf['mail']['client']='smtp'; +$conf['mail']['client']='php'; +$conf['mail']['whitelist']= ""; +$conf['mail']['blacklist']= ""; +$conf['mail']['smtp'] = array(); +$conf['mail']['smtp']['host']="mail.yourdomain.example"; +$conf['mail']['smtp']['host']="locahost"; +$conf['mail']['smtp']['port']="25"; +$conf['mail']['smtp']['auth_username']="your.user@something.example"; +$conf['mail']['smtp']['auth_password']="notsecret"; +$conf['mail']['smtp']['timeout']="45"; +$conf['mail']['smtp']['localhost']='0'; +$conf['mail']['smtp']['localhost']="your.fully.qualified.hostname.example"; +$conf['mail']['smtp']['tls']=false; +$conf['mail']['smtp']['ssl']=false; + +$conf['mime-types'] = array(); +$conf['mime-types']['ez'] = 'application/andrew-inset'; +$conf['mime-types']['csm'] = 'application/cu-seeme'; +$conf['mime-types']['cu'] = 'application/cu-seeme'; +$conf['mime-types']['tsp'] = 'application/dsptype'; +$conf['mime-types']['spl'] = 'application/futuresplash'; +$conf['mime-types']['cpt'] = 'application/mac-compactpro'; +$conf['mime-types']['hqx'] = 'application/mac-binhex40'; +$conf['mime-types']['nb'] = 'application/mathematica'; +$conf['mime-types']['mdb'] = 'application/msaccess'; +$conf['mime-types']['doc'] = 'application/msword'; +$conf['mime-types']['dot'] = 'application/msword'; +$conf['mime-types']['bin'] = 'application/octet-stream'; +$conf['mime-types']['oda'] = 'application/oda'; +$conf['mime-types']['pdf'] = 'application/pdf'; +$conf['mime-types']['pgp'] = 'application/pgp-signature'; +$conf['mime-types']['ps'] = 'application/postscript'; +$conf['mime-types']['ai'] = 'application/postscript'; +$conf['mime-types']['eps'] = 'application/postscript'; +$conf['mime-types']['rtf'] = 'application/rtf'; +$conf['mime-types']['smi'] = 'application/smil'; +$conf['mime-types']['smil'] = 'application/smil'; +$conf['mime-types']['xls'] = 'application/vnd.ms-excel'; +$conf['mime-types']['xlb'] = 'application/vnd.ms-excel'; +$conf['mime-types']['ppt'] = 'application/vnd.ms-powerpoint'; +$conf['mime-types']['pps'] = 'application/vnd.ms-powerpoint'; +$conf['mime-types']['pot'] = 'application/vnd.ms-powerpoint'; +$conf['mime-types']['sdw'] = 'application/vnd.stardivision.writer'; +$conf['mime-types']['sgl'] = 'application/vnd.stardivision.writer-global'; +$conf['mime-types']['vor'] = 'application/vnd.stardivision.writer'; +$conf['mime-types']['sdc'] = 'application/vnd.stardivision.calc'; +$conf['mime-types']['sda'] = 'application/vnd.stardivision.draw'; +$conf['mime-types']['sdd'] = 'application/vnd.stardivision.impress'; +$conf['mime-types']['sdp'] = 'application/vnd.stardivision.impress-packed'; +$conf['mime-types']['smf'] = 'application/vnd.stardivision.math'; +$conf['mime-types']['sds'] = 'application/vnd.stardivision.chart'; +$conf['mime-types']['smd'] = 'application/vnd.stardivision.mail'; +$conf['mime-types']['wbxml'] = 'application/vnd.wap.wbxml '; +$conf['mime-types']['wmlc'] = 'application/vnd.wap.wmlc'; +$conf['mime-types']['wmlsc'] = 'application/vnd.wap.wmlscriptc'; +$conf['mime-types']['wp5'] = 'application/wordperfect5.1'; +$conf['mime-types']['zip'] = 'application/zip'; +$conf['mime-types']['wk'] = 'application/x-123'; +$conf['mime-types']['bcpio'] = 'application/x-bcpio'; +$conf['mime-types']['vcd'] = 'application/x-cdlink '; +$conf['mime-types']['pgn'] = 'application/x-chess-pgn'; +$conf['mime-types']['cpio'] = 'application/x-cpio'; +$conf['mime-types']['csh'] = 'application/x-csh'; +$conf['mime-types']['deb'] = 'application/x-debian-package'; +$conf['mime-types']['dcr'] = 'application/x-director'; +$conf['mime-types']['dir'] = 'application/x-director'; +$conf['mime-types']['dxr'] = 'application/x-director'; +$conf['mime-types']['wad'] = 'application/x-doom'; +$conf['mime-types']['dms'] = 'application/x-dms'; +$conf['mime-types']['dvi'] = 'application/x-dvi'; +$conf['mime-types']['pfa'] = 'application/x-font'; +$conf['mime-types']['pfb'] = 'application/x-font'; +$conf['mime-types']['gsf'] = 'application/x-font'; +$conf['mime-types']['pcf'] = 'application/x-font'; +$conf['mime-types']['spl'] = 'application/x-futuresplash '; +$conf['mime-types']['gnumeric'] = 'application/x-gnumeric'; +$conf['mime-types']['gtar'] = 'application/x-gtar'; +$conf['mime-types']['tgz'] = 'application/x-gtar'; +$conf['mime-types']['taz'] = 'application/x-gtar'; +$conf['mime-types']['hdf'] = 'application/x-hdf'; +$conf['mime-types']['phtml'] = 'text/html'; +$conf['mime-types']['pht'] = 'text/html'; +$conf['mime-types']['php'] = 'text/html'; +$conf['mime-types']['phps'] = 'text/html'; +$conf['mime-types']['php3'] = 'text/html'; +$conf['mime-types']['php3p'] = 'text/html '; +$conf['mime-types']['php4'] = 'text/html'; +$conf['mime-types']['docbook'] = 'application/docbook+xml'; +$conf['mime-types']['ica'] = 'application/x-ica'; +$conf['mime-types']['jar'] = 'application/x-java-archive'; +$conf['mime-types']['jnlp'] = 'application/x-java-jnlp-file'; +$conf['mime-types']['ser'] = 'application/x-java-serialized-object'; +$conf['mime-types']['class'] = 'application/x-java-vm'; +$conf['mime-types']['js'] = 'application/x-javascript'; +$conf['mime-types']['chrt'] = 'application/x-kchart'; +$conf['mime-types']['kil'] = 'application/x-killustrator'; +$conf['mime-types']['kpr'] = 'application/x-kpresenter'; +$conf['mime-types']['kpt'] = 'application/x-kpresenter'; +$conf['mime-types']['skp'] = 'application/x-koan '; +$conf['mime-types']['skd'] = 'application/x-koan '; +$conf['mime-types']['skt'] = 'application/x-koan '; +$conf['mime-types']['skm'] = 'application/x-koan '; +$conf['mime-types']['ksp'] = 'application/x-kspread'; +$conf['mime-types']['kwd'] = 'application/x-kword'; +$conf['mime-types'][' kwt'] = 'application/x-kword'; +$conf['mime-types']['latex'] = 'application/x-latex'; +$conf['mime-types']['lha'] = 'application/x-lha'; +$conf['mime-types']['lzh'] = 'application/x-lzh'; +$conf['mime-types']['lzx'] = 'application/x-lzx'; +$conf['mime-types']['frm'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['maker'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['frame'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['fm'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['fb'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['book'] = 'fbdocapplication/x-maker'; +$conf['mime-types']['mif'] = 'application/x-mif'; +$conf['mime-types']['com'] = 'application/x-msdos-program'; +$conf['mime-types']['exe'] = 'application/x-msdos-program'; +$conf['mime-types']['bat'] = 'application/x-msdos-program'; +$conf['mime-types']['dll'] = 'application/x-msdos-program'; +$conf['mime-types']['msi'] = 'application/x-msi'; +$conf['mime-types']['nc'] = 'application/x-netcdf'; +$conf['mime-types']['cdf'] = 'application/x-netcdf'; +$conf['mime-types']['pac'] = 'application/x-ns-proxy-autoconfig'; +$conf['mime-types']['o'] = 'application/x-object'; +$conf['mime-types']['ogg'] = 'application/x-ogg'; +$conf['mime-types']['oza'] = 'application/x-oz-application'; +$conf['mime-types']['pl'] = 'application/x-perl'; +$conf['mime-types']['pm'] = 'application/x-perl'; +$conf['mime-types']['crl'] = 'application/x-pkcs7-crl'; +$conf['mime-types']['rpm'] = 'application/x-redhat-package-manager'; +$conf['mime-types']['shar'] = 'application/x-shar'; +$conf['mime-types']['swf'] = 'application/x-shockwave-flash'; +$conf['mime-types']['swfl'] = 'application/x-shockwave-flash'; +$conf['mime-types']['sh'] = 'application/x-sh '; +$conf['mime-types']['sit'] = 'application/x-stuffit'; +$conf['mime-types']['sv4cpio'] = 'application/x-sv4cpio'; +$conf['mime-types']['sv4crc'] = 'application/x-sv4crc'; +$conf['mime-types']['tar'] = 'application/x-tar'; +$conf['mime-types']['tcl'] = 'application/x-tcl'; +$conf['mime-types']['tex'] = 'application/x-tex'; +$conf['mime-types']['gf'] = 'application/x-tex-gf'; +$conf['mime-types']['pk'] = 'application/x-tex-pk'; +$conf['mime-types']['texinfo'] = 'application/x-texinfo'; +$conf['mime-types']['texi'] = 'application/x-texinfo'; +$conf['mime-types']['; "~"'] = 'application/x-trash'; +$conf['mime-types'][';"%"'] = 'application/x-trash'; +$conf['mime-types']['bak'] = 'application/x-trash'; +$conf['mime-types']['old'] = 'application/x-trash'; +$conf['mime-types']['sik'] = 'application/x-trash'; +$conf['mime-types']['t'] = 'application/x-troff'; +$conf['mime-types']['tr'] = 'application/x-troff'; +$conf['mime-types']['roff'] = 'application/x-troff'; +$conf['mime-types']['man'] = 'application/x-troff-man'; +$conf['mime-types']['me'] = 'application/x-troff-me'; +$conf['mime-types']['ms'] = 'application/x-troff-ms'; +$conf['mime-types']['ustar'] = 'application/x-ustar'; +$conf['mime-types']['src'] = 'application/x-wais-source'; +$conf['mime-types']['wz'] = 'application/x-wingz'; +$conf['mime-types']['crt'] = 'application/x-x509-ca-cert'; +$conf['mime-types']['fig'] = 'application/x-xfig'; +$conf['mime-types']['au'] = 'audio/basic'; +$conf['mime-types']['snd'] = 'audio/basic'; +$conf['mime-types']['mid'] = 'audio/midi'; +$conf['mime-types']['midi'] = 'audio/midi'; +$conf['mime-types']['kar'] = 'audio/midi'; +$conf['mime-types']['mpga'] = 'audio/mpeg'; +$conf['mime-types']['mpega'] = 'audio/mpeg'; +$conf['mime-types']['mp2'] = 'audio/mpeg'; +$conf['mime-types']['mp3'] = 'audio/mpeg'; +$conf['mime-types']['m3u'] = 'audio/mpegurl'; +$conf['mime-types']['sid'] = 'audio/prs.sid'; +$conf['mime-types']['aif'] = 'audio/x-aiff'; +$conf['mime-types']['aiff'] = 'audio/x-aiff'; +$conf['mime-types']['aifc'] = 'audio/x-aiff'; +$conf['mime-types']['gsm'] = 'audio/x-gsm'; +$conf['mime-types']['m3u'] = 'audio/x-mpegurl'; +$conf['mime-types']['rpm'] = 'audio/x-pn-realaudio-plugin '; +$conf['mime-types']['ra'] = 'audio/x-pn-realaudio'; +$conf['mime-types']['rm'] = 'audio/x-pn-realaudio'; +$conf['mime-types']['ram'] = 'audio/x-pn-realaudio'; +$conf['mime-types']['ra'] = 'audio/x-realaudio '; +$conf['mime-types']['pls'] = 'audio/x-scpls'; +$conf['mime-types']['wav'] = 'audio/x-wav'; +$conf['mime-types']['pdb'] = 'chemical/x-pdb'; +$conf['mime-types']['xyz'] = 'chemical/x-xyz '; +$conf['mime-types']['bmp'] = 'image/bmp'; +$conf['mime-types']['gif'] = 'image/gif'; +$conf['mime-types']['ief'] = 'image/ief'; +$conf['mime-types']['jpeg'] = 'image/jpeg'; +$conf['mime-types']['jpg'] = 'image/jpeg'; +$conf['mime-types']['jpe'] = 'image/jpeg'; +$conf['mime-types']['pcx'] = 'image/pcx'; +$conf['mime-types']['png'] = 'image/png'; +$conf['mime-types']['svg'] = 'image/svg+xml'; +$conf['mime-types']['svgz'] = 'image/svg+xml'; +$conf['mime-types']['tiff'] = 'image/tiff'; +$conf['mime-types']['tif'] = 'image/tiff'; +$conf['mime-types']['wbmp'] = 'image/vnd.wap.wbmp'; +$conf['mime-types']['ras'] = 'image/x-cmu-raster'; +$conf['mime-types']['cdr'] = 'image/x-coreldraw'; +$conf['mime-types']['pat'] = 'image/x-coreldrawpattern'; +$conf['mime-types']['cdt'] = 'image/x-coreldrawtemplate'; +$conf['mime-types']['cpt'] = 'image/x-corelphotopaint'; +$conf['mime-types']['djvu'] = 'image/x-djvu'; +$conf['mime-types']['djv'] = 'image/x-djvu'; +$conf['mime-types']['jng'] = 'image/x-jng'; +$conf['mime-types']['bmp'] = 'image/x-ms-bmp'; +$conf['mime-types']['pnm'] = 'image/x-portable-anymap'; +$conf['mime-types']['pbm'] = 'image/x-portable-bitmap'; +$conf['mime-types']['pgm'] = 'image/x-portable-graymap'; +$conf['mime-types']['ppm'] = 'image/x-portable-pixmap'; +$conf['mime-types']['rgb'] = 'image/x-rgb'; +$conf['mime-types']['xbm'] = 'image/x-xbitmap'; +$conf['mime-types']['xpm'] = 'image/x-xpixmap'; +$conf['mime-types']['xwd'] = 'image/x-xwindowdump'; +$conf['mime-types']['igs'] = 'model/iges'; +$conf['mime-types']['iges'] = 'model/iges'; +$conf['mime-types']['msh'] = 'model/mesh'; +$conf['mime-types']['mesh'] = 'model/mesh'; +$conf['mime-types']['silo'] = 'model/mesh'; +$conf['mime-types']['wrl'] = 'model/vrml'; +$conf['mime-types']['vrml'] = 'model/vrml'; +$conf['mime-types']['csv'] = 'text/comma-separated-values'; +$conf['mime-types']['css'] = 'text/css'; +$conf['mime-types']['htm'] = 'text/html'; +$conf['mime-types']['html'] = 'text/html'; +$conf['mime-types']['xhtml'] = 'text/html'; +$conf['mime-types']['mml'] = 'text/mathml'; +$conf['mime-types']['asc'] = 'text/plain'; +$conf['mime-types']['txt'] = 'text/plain'; +$conf['mime-types']['text'] = 'text/plain'; +$conf['mime-types']['diff'] = 'text/plain'; +$conf['mime-types']['rtx'] = 'text/richtext'; +$conf['mime-types']['rtf'] = 'text/rtf'; +$conf['mime-types']['tsv'] = 'text/tab-separated-values'; +$conf['mime-types']['wml'] = 'text/vnd.wap.wml'; +$conf['mime-types']['wmls'] = 'text/vnd.wap.wmlscript'; +$conf['mime-types']['xml'] = 'text/xml'; +$conf['mime-types']['xsl'] = 'text/xml'; +$conf['mime-types']['hpp'] = 'text/x-c++hdr'; +$conf['mime-types']['hxx'] = 'text/x-c++hdr'; +$conf['mime-types']['hh'] = 'text/x-c++hdr'; +$conf['mime-types']['cpp'] = 'text/x-c++src'; +$conf['mime-types']['cxx'] = 'text/x-c++src'; +$conf['mime-types']['cc'] = 'text/x-c++src'; +$conf['mime-types']['h'] = 'text/x-chdr'; +$conf['mime-types']['csh'] = 'text/x-csh'; +$conf['mime-types']['c'] = 'text/x-csrc'; +$conf['mime-types']['java'] = 'text/x-java'; +$conf['mime-types']['moc'] = 'text/x-moc'; +$conf['mime-types']['p'] = 'text/x-pascal'; +$conf['mime-types']['pas'] = 'text/x-pascal'; +$conf['mime-types']['etx'] = 'text/x-setext'; +$conf['mime-types']['sh'] = 'text/x-sh'; +$conf['mime-types']['tcl'] = 'text/x-tcl'; +$conf['mime-types']['tk'] = 'text/x-tcl'; +$conf['mime-types']['tex'] = 'text/x-tex'; +$conf['mime-types']['ltx'] = 'text/x-tex'; +$conf['mime-types']['sty'] = 'text/x-tex'; +$conf['mime-types']['cls'] = 'text/x-tex'; +$conf['mime-types']['vcs'] = 'text/x-vcalendar'; +$conf['mime-types']['vcf'] = 'text/x-vcard'; +$conf['mime-types']['dl'] = 'video/dl'; +$conf['mime-types']['fli'] = 'video/fli'; +$conf['mime-types']['gl'] = 'video/gl'; +$conf['mime-types']['mpeg'] = 'video/mpeg'; +$conf['mime-types']['mpg'] = 'video/mpeg'; +$conf['mime-types']['mpe'] = 'video/mpeg'; +$conf['mime-types']['qt'] = 'video/quicktime'; +$conf['mime-types']['mov'] = 'video/quicktime'; +$conf['mime-types']['mxu'] = 'video/vnd.mpegurl'; +$conf['mime-types']['mng'] = 'video/x-mng'; +$conf['mime-types']['asf'] = 'video/x-ms-asf'; +$conf['mime-types']['asx'] = 'video/x-ms-asf'; +$conf['mime-types']['avi'] = 'video/x-msvideo'; +$conf['mime-types']['movie'] = 'video/x-sgi-movie'; +$conf['mime-types']['ice'] = 'x-conference/x-cooltalk'; +$conf['mime-types']['vrm'] = 'x-world/x-vrml'; +$conf['mime-types']['vrml'] = 'x-world/x-vrml'; +$conf['mime-types']['wrl'] = 'x-world/x-vrml'; + +$conf['publish'] = array(); +$conf['publish']['edit']=true; +$conf['publish']['default']='index'; +$conf['publish']['format']= "{filename}{language_sep}{language}{type_sep}{type}"; +$conf['publish']['language_sep']= "."; +$conf['publish']['type_sep']= "."; +$conf['publish']['filename_language']='auto'; +$conf['publish']['filename_type']='always'; +$conf['publish']['style']="id"; +$conf['publish']['url']='relative'; +$conf['publish']['url']='absolute'; +$conf['publish']['enable_php_in_page_content']=false; +$conf['publish']['enable_php_in_file_content']=false; +$conf['publish']['escape_8bit_characters']=false; +$conf['publish']['encode_utf8_in_html']=true; +$conf['publish']['negotiation'] = array(); +$conf['publish']['negotiation']['page_negotiate_type']=true; +$conf['publish']['negotiation']['page_negotiate_language']=true; +$conf['publish']['negotiation']['file_negotiate_type']=true; +$conf['publish']['filesystem'] = array(); +$conf['publish']['filesystem']['per_project']=true; +$conf['publish']['filesystem']['directory']='/var/www/'; +$conf['publish']['command'] = array(); +$conf['publish']['command']['per_project']=true; +$conf['publish']['command']['enable']=false; +$conf['publish']['command']['command']=''; +$conf['publish']['ftp'] = array(); +$conf['publish']['ftp']['enable']=true; +$conf['publish']['ftp']['per_project']=true; +$conf['publish']['ftp']['port']='21'; +$conf['publish']['ftp']['host']=''; +$conf['publish']['ftp']['path']=''; +$conf['publish']['ftp']['user']='anonymous'; +$conf['publish']['ftp']['pass']='mail@example.com'; +$conf['replace'] = array(); +$conf['replace']['']='0'; +$conf['replace']['']='0'; +$conf['replace']['euro']= "EUR,&euro;"; +$conf['replace']['copy']= "(c),&copy;"; +$conf['search'] = array(); +$conf['search']['']='0'; +$conf['search']['quicksearch'] = array(); +$conf['search']['quicksearch']['flag'] = array(); +$conf['search']['quicksearch']['flag']['id']=true; +$conf['search']['quicksearch']['flag']['name']=true; +$conf['search']['quicksearch']['flag']['filename']=true; +$conf['search']['quicksearch']['flag']['description']=true; +$conf['search']['quicksearch']['flag']['content']=false; +$conf['security'] = array(); +$conf['security']['readonly']=false; +$conf['security']['nopublish']=false; +$conf['security']['umask']='0'; +$conf['security']['chmod']='0'; +$conf['security']['chmod_dir']='0'; +$conf['security']['']='0'; +$conf['security']['disable_dynamic_code']=true; +$conf['security']['show_system_info']=true; +$conf['security']['use_post_token']=true; +$conf['security']['renew_session_login']=false; +$conf['security']['renew_session_logout']=false; +$conf['security']['default'] = array(); +$conf['security']['default']['username']=''; +$conf['security']['default']['password']=''; +$conf['security']['guest'] = array(); +$conf['security']['guest']['enable']=false; +$conf['security']['guest']['user']='guest'; +$conf['security']['login'] = array(); +$conf['security']['login']['type']='form'; +$conf['security']['auth'] = array(); +$conf['security']['auth']['type']='database'; +$conf['security']['auth']['userdn']=false; +$conf['security']['authorize'] = array(); +$conf['security']['authorize']['type']='database'; +$conf['security']['authorize']['type']='ldap'; + +$conf['security']['modules'] = array(); +$conf['security']['modules']['autologin']='Remember,Guest,SingleSignon'; +$conf['security']['modules']['preselect']='Ident,SSL,Cookie'; +$conf['security']['modules']['authenticate']='LdapUserDN,Database,Internal'; + +$conf['security']['newuser'] = array(); +$conf['security']['newuser']['autoadd'] = true; +$conf['security']['newuser']['autogroups'] = ""; + +$conf['security']['password'] = array(); +$conf['security']['password']['random_length']=10; +$conf['security']['password']['min_length']=6; +$conf['security']['password']['pepper']= ''; +$conf['security']['password']['deny_after_expiration_duration'] = 72; +$conf['security']['password']['force_change_if_cleartext']= true; +$conf['security']['http'] = array(); +$conf['security']['http']['url']= "http://example.net/restricted-area"; +$conf['security']['authdb'] = array(); +$conf['security']['authdb']['enable'] = false; +$conf['security']['authdb']['type']='postgresql'; +$conf['security']['authdb']['user']='dbuser'; +$conf['security']['authdb']['password']='dbpassword'; +$conf['security']['authdb']['host']= '127.0.0.1'; +$conf['security']['authdb']['database']='dbname'; +$conf['security']['authdb']['persistent']=false; +$conf['security']['authdb']['prepare']=false; +$conf['security']['authdb']['sql']= "select 1 from table where user={username} and password={password}"; +$conf['security']['authdb']['hash_algo']='md5'; +$conf['security']['authdb']['add']=true; +$conf['security']['ssl'] = array(); +$conf['security']['ssl']['trust']=false; +$conf['security']['ssl']['client_cert_dn_env'] = 'SSL_CLIENT_S_DN_CN'; +$conf['security']['openid'] = array(); +$conf['security']['openid']['enable']=false; +$conf['security']['openid']['add']=false; +$conf['security']['openid']['logo_url']='0'; +$conf['security']['openid']['logo_url']="http://openid.net/login-bg.gif"; +$conf['security']['openid']['trust_root']='http://your.server.example/openrat/'; +$conf['security']['openid']['trust_root']='0'; +$conf['security']['openid']['trusted_server']='openid1.example.com,openid2.example.com'; +$conf['security']['openid']['trusted_server']='0'; +$conf['security']['openid']['update_user']=true; +$conf['security']['openid']['user_identity']=true; +$conf['security']['openid']['provider']['name']='google'; +$conf['security']['openid']['provider']['google']['xrds_uri']="http://google.com/accounts/o8/id"; +$conf['security']['openid']['provider']['google']['map_attribute']="email"; +$conf['security']['openid']['provider']['google']['name']="Google"; +$conf['security']['openid']['provider']['google']['map_internal']="mail"; +$conf['security']['openid']['provider']['yahoo']['xrds_uri']="http://??????"; +$conf['security']['openid']['provider']['yahoo']['map_attribute']="usename"; +$conf['security']['openid']['provider']['yahoo']['map_internal']="mail"; +$conf['security']['sso'] = array(); +$conf['security']['sso']['enable']=false; +$conf['security']['sso']['url']="http://localhost/check.php?phpsessid={id}&check=true"; +$conf['security']['sso']['url']="https://www.example.com/phpmyadmin/main.php?server=1"; +$conf['security']['sso']['auth_param_name']='authid'; +$conf['security']['sso']['auth_param_serialized']=true; +$conf['security']['sso']['cookie']=true; +$conf['security']['sso']['cookie_name']='0'; +$conf['security']['sso']['force']=true; +$conf['security']['sso']['expect']='0'; +$conf['security']['sso']['expect_regexp']="/running on/"; +$conf['security']['sso']['username_regexp']="/running on localhost as ([a-z]+)@localhost/"; +$conf['security']['logout'] = array(); +$conf['security']['logout']['redirect_url']="http://your.intranet.example/"; +$conf['security']['logout']['redirect_url']='0'; +$conf['security']['user'] = array(); +$conf['security']['user']['show_admin_mail']=true; +$conf['security']['user']['show_mail']=true; +$conf['security']['user']['send_message']=true; +$conf['security']['content-security-policy']=true; + +$conf['style-default'] = array(); +$conf['style-default']['name']='Unnamed'; +$conf['style-default']['title_background_color']='grey'; +$conf['style-default']['title_text_color']='white'; +$conf['style-default']['text_color' ]= 'black'; +$conf['style-default']['background_color' ]= '#d9d9d9'; +$conf['style-default']['inactive_background_color' ]= 'silver'; + + +$conf['style'] = array(); +$conf['style']['earlgrey']=array(); +$conf['style']['earlgrey']['name']='Earl grey'; +$conf['style']['earlgrey']['title_background_color']='grey'; +$conf['style']['earlgrey']['title_text_color']='white'; +$conf['style']['earlgrey']['text_color'] ='black'; +$conf['style']['earlgrey']['background_color'] = '#e9e9e9'; +$conf['style']['earlgrey']['inactive_background_color'] = 'silver'; + +// $conf['style']['system']=array(); +// $conf['style']['system']['name']='System colors'; +// $conf['style']['system']['title_background_color']='Menu'; +// $conf['style']['system']['title_text_color']='MenuText'; +// $conf['style']['system']['text_color'] ='WindowText'; +// $conf['style']['system']['background_color'] = 'Background'; +// $conf['style']['system']['inactive_background_color'] = 'WindowFrame'; + +$conf['style']['modern']=array(); +$conf['style']['modern']['name']='Blue sky'; +$conf['style']['modern']['title_background_color']='#3F6194'; +$conf['style']['modern']['title_text_color']='white'; +$conf['style']['modern']['text_color'] ='black'; +$conf['style']['modern']['background_color'] = '#F3F3F3'; +$conf['style']['modern']['inactive_background_color'] = '#CCCCCC'; + +$conf['style']['moorweide']=array(); +$conf['style']['moorweide']['name']='Moorweide'; +$conf['style']['moorweide']['title_background_color']='#006633'; +$conf['style']['moorweide']['title_text_color']='white'; +$conf['style']['moorweide']['text_color'] ='black'; +$conf['style']['moorweide']['background_color'] = '#F5FFFA'; +$conf['style']['moorweide']['inactive_background_color'] = '#CEE6DA'; + +$conf['style']['dark']=array(); +$conf['style']['dark']['name']='Dark'; +$conf['style']['dark']['title_background_color']='#868685'; +$conf['style']['dark']['title_text_color']='#DCDCDC'; +$conf['style']['dark']['text_color'] ='#FFFFFF'; +$conf['style']['dark']['background_color'] = '#201F1D'; +$conf['style']['dark']['inactive_background_color'] = '#868685'; + +$conf['theme'] = array(); +$conf['theme']['compiler'] = array(); +$conf['theme']['compiler']['enable']=true; +$conf['theme']['compiler']['cache']=true; +$conf['theme']['compiler']['chmod']=''; +$conf['theme']['compiler']['compile_at_logout']=false; +$conf['wiki'] = array(); +$conf['wiki']['convert_html']=true; +$conf['wiki']['convert_bbcode']=true; +$conf['wiki']['tag_strong']= "*"; +$conf['wiki']['tag_emphatic']= "_"; + +$conf['application']['name' ] = OR_TITLE; +$conf['application']['version' ] = OR_VERSION; +$conf['application']['operator'] = OR_TITLE; +$conf['production']= true; + +?>+ \ No newline at end of file diff --git a/modules/util/exception/OpenRatException.class.php b/modules/util/exception/OpenRatException.class.php @@ -0,0 +1,25 @@ +<?php + +class OpenRatException extends Exception +{ + public $key; + + // Die Exception neu definieren, damit die Mitteilung nicht optional ist + public function __construct($key, $message, $code = 0, Exception $previous = null) { + + $this->key = $key; + + // sicherstellen, dass alles korrekt zugewiesen wird + parent::__construct($message, $code, $previous); + } + + // maßgeschneiderte Stringdarstellung des Objektes + public function __toString() { + return __CLASS__ . ": ".$this->key." [{$this->code}]: '{$this->message}' in {$this->file}({$this->line})\n" + . "{$this->getTraceAsString()}\n"; + } + +} + + +?>+ \ No newline at end of file diff --git a/modules/util/exception/SecurityException.class.php b/modules/util/exception/SecurityException.class.php @@ -0,0 +1,9 @@ +<?php + +class SecurityException extends RuntimeException +{ + +} + + +?>+ \ No newline at end of file diff --git a/modules/util/require.php b/modules/util/require.php @@ -1,5 +1,33 @@ <?php -//include( dirname(__FILE__) . '/.class.php'); +require_once( __DIR__.'/'."GlobalFunctions.class.".PHP_EXT ); +require_once( __DIR__.'/'."Http.class.".PHP_EXT ); +require_once( __DIR__.'/'."Html.class.".PHP_EXT ); +require_once( __DIR__.'/'."Text.class.".PHP_EXT ); +require_once( __DIR__.'/'."Mail.class.".PHP_EXT ); +require_once( __DIR__.'/'."Ldap.class.".PHP_EXT ); +require_once( __DIR__.'/'."FileUtils.class.".PHP_EXT ); +require_once( __DIR__.'/'."JSON.class.".PHP_EXT ); +require_once( __DIR__.'/'."Less.".PHP_EXT ); +require_once( __DIR__.'/'."JSqueeze.class.".PHP_EXT ); +require_once( __DIR__.'/'."Spyc.class.".PHP_EXT ); +require_once( __DIR__.'/'."exception/OpenRatException.class.".PHP_EXT ); +require_once( __DIR__.'/'."exception/SecurityException.class.".PHP_EXT ); +require_once( __DIR__.'/'."TreeElement.class.".PHP_EXT ); +require_once( __DIR__.'/'."AbstractTree.class.".PHP_EXT ); +require_once( __DIR__.'/'."AdministrationTree.class.".PHP_EXT ); +require_once( __DIR__.'/'."ProjectTree.class.".PHP_EXT ); +require_once( __DIR__.'/'."Publish.class.".PHP_EXT ); +require_once( __DIR__.'/'."Ftp.class.".PHP_EXT ); +require_once( __DIR__.'/'."Macro.class.".PHP_EXT ); +require_once( __DIR__.'/'."Dynamic.class.".PHP_EXT ); +require_once( __DIR__.'/'."Api.class.".PHP_EXT ); +require_once( __DIR__.'/'."Code.class.".PHP_EXT ); +require_once( __DIR__.'/'."Transformer.class.".PHP_EXT ); +require_once( __DIR__.'/'."Line.class.".PHP_EXT ); +require_once( __DIR__.'/'."Upload.class.".PHP_EXT ); +require_once( __DIR__.'/'."Upload.class.".PHP_EXT ); +require_once( __DIR__.'/'."ArchiveTar.class.".PHP_EXT ); +require_once( __DIR__.'/'."ArchiveUnzip.class.".PHP_EXT ); +require_once( __DIR__.'/'."ArchiveZip.class.".PHP_EXT ); -?>- \ No newline at end of file diff --git a/util/.htaccess b/util/.htaccess @@ -1,2 +0,0 @@ -order deny,allow -deny from all- \ No newline at end of file diff --git a/util/AbstractTree.class.php b/util/AbstractTree.class.php @@ -1,171 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Darstellen einer Baumstruktur mit Funktion zum Laden, Oeffnen und Schliessen - * von Teilbaeumen - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class AbstractTree -{ - /** - * Alle Elemente des Baumes - */ - var $elements = array(); - - var $tempElements = array(); - var $userIsAdmin = false; - - var $autoOpen = array(0,1); - - var $opened = array(); - - /** - * Hoechste Element-Id - * @type Integer - */ - var $maxId; - - // Konstruktor - function AbstractTree() - { - // Feststellen, ob der angemeldete Benutzer ein Administrator ist - $user = Session::getUser(); - $this->userIsAdmin = $user->isAdmin; - - // Wurzel-Element laden - $this->root(); - $this->elements[0] = $this->tempElements[0]; - $this->tempElements = array(); - $this->maxId = 0; - - foreach( $this->autoOpen as $openId ) - $this->open($openId); - } - - function refresh() { - - $this->elements = array(); - - // Wurzel-Element laden - $this->root(); - $this->elements[0] = $this->tempElements[0]; - $this->tempElements = array(); - $this->maxId = 0; - - $oids = $this->opened; - $this->opened = array(); - foreach( $oids as $oid) - { - if ( isset($this->elements[$oid]) ) - $this->open($oid); - } - } - - - - function all() { - - $this->elements = array(); - $this->opened = array(); - - // Wurzel-Element laden - $this->root(); - $this->elements[0] = $this->tempElements[0]; - $this->tempElements = array(); - $this->maxId = 0; - - for( $eid=0;isset($this->elements[$eid]); $eid++) - { - $this->open($eid); - } - } - - - /** - * Oeffnen eines Teilbaumes. Es wird der eindeutige Name des zu oeffnenden Teilbaumes als - * Parameter uebergeben - * @param elementName der Name des Elementes, welches zu oeffnen ist - */ - function open( $elementId ) - { - $k = array_search($elementId,$this->opened); - if ( $k !== false ) - return; // Ist schon offen. Evtl. Reload gedrückt? - - $this->opened[] = $elementId; - if ( ! isset($this->elements[$elementId]) ) - return; - $funcName = $this->elements[$elementId]->type; - if ( empty($funcName) ) - return; - - $this->$funcName( $this->elements[$elementId]->internalId ); - - // Wenn keine Unterelemente gefunden, dann die ?ffnen-Funktion deaktivieren - if ( count( $this->tempElements ) == 0 ) - $this->elements[$elementId]->type = ''; - - foreach( $this->tempElements as $treeElement ) - { - $this->maxId++; - $this->elements[$elementId]->subElementIds[] = $this->maxId; - $this->elements[$this->maxId] = $treeElement; - } - - if ( count($this->tempElements)==1 ) - { - $this->tempElements = array(); - $this->open($this->maxId); - } - - $this->tempElements = array(); - } - - - /** - * Schliessen eines Teilbaumes - * @param elementName der Name des Elementes, welches zu schliessen ist - */ - - function close( $elementId ) - { - $this->elements[$elementId]->subElementIds = array(); - - $k = array_search($elementId,$this->opened); - if ( $k !== false ) - unset($this->opened[$k]); - } - - - /** - * Hinzufuegen eines Baum-Elementes - * @param TreeElement Hinzuzufuegendes Baumelement - */ - function addTreeElement( $treeElement ) - { - $this->tempElements[] = $treeElement; - } - - -} - -?>- \ No newline at end of file diff --git a/util/AdministrationTree.class.php b/util/AdministrationTree.class.php @@ -1,594 +0,0 @@ -<?php -use cms\model\User; -use cms\model\Project; -use cms\model\Group; -use cms\model\Folder; - -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Darstellen einer Baumstruktur mit Administrationfunktionen - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class AdministrationTree extends AbstractTree -{ - /** - * Alle Elemente des Baumes - */ - var $elements; - var $confCache = array(); - - function root() - { - if ( !$this->userIsAdmin ) - Http::notAuthorized('Administration-Tree is only visible for admins.'); - -// $treeElement = new TreeElement(); -// $treeElement->text = lang('GLOBAL_ADMINISTRATION'); -// $treeElement->description = lang('GLOBAL_ADMINISTRATION'); -// $treeElement->type = 'administration'; -// $treeElement->icon = 'administration'; - - $this->administration(); - - $this->autoOpen[] = 1; - } - - - - function administration() - { - global $conf; - $conf_config = $conf['interface']['config']; - - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_PROJECTS'); - $treeElement->description = lang('GLOBAL_PROJECTS'); - $treeElement->url = Html::url('projectlist','show',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'projectlist'; - $treeElement->icon = 'projectlist'; - $treeElement->type = 'projects'; - $treeElement->target = 'cms_main'; - - $this->addTreeElement( $treeElement ); - - - $treeElement = new TreeElement(); - $treeElement->text = lang('USER_AND_GROUPS'); - $treeElement->description = lang('USER_AND_GROUPS'); - $treeElement->icon = 'userlist'; - $treeElement->type = 'userandgroups'; - - $this->addTreeElement( $treeElement ); -// $this->userandgroups(0);; - - if ( $conf_config['enable'] ) - { - $treeElement = new TreeElement(); - $treeElement->text = lang('PREFERENCES'); - $treeElement->description = lang('PREFERENCES'); - $treeElement->icon = 'configuration'; - //$treeElement->type = 'prefs'; - $treeElement->action = 'configuration'; - - $this->addTreeElement( $treeElement ); - } - - // Wechseln zu: Projekte... - /* - foreach( Project::getAll() as $id=>$name ) - { - $treeElement = new TreeElement(); - - $treeElement->text = lang('PROJECT').' '.$name; - $treeElement->url = Html::url(array('action' =>'tree', - 'subaction' =>'reload', - 'projectid' =>$id )); - $treeElement->icon = 'project'; - $treeElement->description = ''; - $treeElement->target = 'cms_tree'; - - $this->addTreeElement( $treeElement ); - } - */ - } - - - - function userandgroups( ) - { - $treeElement = new TreeElement(); - $treeElement->text = lang('GLOBAL_USER'); - $treeElement->description = lang('GLOBAL_USER'); - $treeElement->url = Html::url('user','listing',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'userlist'; - $treeElement->icon = 'userlist'; - $treeElement->target = 'cms_main'; - $treeElement->type = 'users'; - - $this->addTreeElement( $treeElement ); - - $treeElement = new TreeElement(); - $treeElement->text = lang('GLOBAL_GROUPS'); - $treeElement->description = lang('GLOBAL_GROUPS'); - $treeElement->url = Html::url('group','listing',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'grouplist'; - $treeElement->icon = 'userlist'; - $treeElement->target = 'cms_main'; - $treeElement->type = 'groups'; - - $this->addTreeElement( $treeElement ); - } - - - function projects( ) - { - // Schleife ueber alle Projekte - foreach(Project::getAllProjects() as $id=> $name ) - { - $treeElement = new TreeElement(); - - $treeElement->internalId = $id; - $treeElement->id = $id; - $treeElement->text = $name; - $treeElement->url = Html::url('project','edit',$id,array(REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'project'; - $treeElement->action = 'project'; - $treeElement->type = 'project'; - $treeElement->description = ''; - $treeElement->target = 'cms_main'; - - $this->addTreeElement( $treeElement ); - } - } - - - - function project( $projectid ) - { - $project = new Project( $projectid ); - - // Hoechster Ordner der Projektstruktur - $folder = new Folder( $project->getRootObjectId() ); - $folder->load(); - - - // Ermitteln, ob der Benutzer Projektadministrator ist - // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern. - if ( $folder->hasRight( ACL_PROP ) ) - $this->userIsProjectAdmin = true; - - if ( $folder->hasRight( ACL_READ ) ) - { - $treeElement = new TreeElement(); - $treeElement->id = $folder->objectid; - // $treeElement->text = $folder->name; - $treeElement->text = lang('FOLDER_ROOT'); - $treeElement->description = lang('FOLDER_ROOT_DESC'); - $treeElement->icon = 'folder'; - $treeElement->action = 'folder'; - $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') ); - $treeElement->target = 'content'; - $treeElement->type = 'folder'; - $treeElement->internalId = $folder->objectid; - $this->addTreeElement( $treeElement ); - } - - - if ( $this->userIsProjectAdmin ) - { - // Templates - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_TEMPLATES'); - $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->description= lang('GLOBAL_TEMPLATES_DESC'); - $treeElement->icon = 'templatelist'; - $treeElement->action = 'templatelist'; - $treeElement->target = 'content'; - $treeElement->type = 'templates'; - $this->addTreeElement( $treeElement ); - } - - - // Sprachen - $treeElement = new TreeElement(); - $treeElement->description= ''; - $treeElement->id = 0; - $treeElement->action = 'languagelist'; - $treeElement->text = lang('GLOBAL_LANGUAGES'); - $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'languagelist'; - $treeElement->description= lang('GLOBAL_LANGUAGES_DESC'); - $treeElement->target = 'content'; - - // Nur fuer Projekt-Administratoren aufklappbar - if ( $this->userIsProjectAdmin ) - $treeElement->type = 'languages'; - - $this->addTreeElement( $treeElement ); - - - // Projektmodelle - $treeElement = new TreeElement(); - $treeElement->description= ''; - - // Nur fuer Projekt-Administratoren aufklappbar - if ( $this->userIsProjectAdmin ) - $treeElement->type = 'models'; - - $treeElement->id = 0; - $treeElement->description= lang('GLOBAL_MODELS_DESC'); - $treeElement->text = lang('GLOBAL_MODELS'); - $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'modellist'; - $treeElement->icon = 'modellist'; - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - - // Sonstiges - // $treeElement = new TreeElement(); - // $treeElement->text = lang('GLOBAL_OTHER'); - // $treeElement->description= lang('GLOBAL_OTHER_DESC'); - // $treeElement->icon = 'other'; - // $treeElement->type = 'other'; - // $this->addTreeElement( $treeElement ); - - // Suche - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_SEARCH'); - $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'search'; - $treeElement->icon = 'search'; - $treeElement->description = lang('GLOBAL_SEARCH_DESC'); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - } - - - - function prefs_system( ) - { - $system = array( 'time' => date('r'), - 'os' => php_uname('s'), - 'host' => php_uname('n'), - 'release'=> php_uname('r'), - 'machine'=> php_uname('m'), - 'owner' => get_current_user(), - 'pid' => getmypid() ); - - foreach( $system as $key=>$value ) - { - $treeElement = new TreeElement(); - $treeElement->text = $key.'='.$value; - $treeElement->icon = 'config_property'; - $this->addTreeElement( $treeElement ); - $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); - } - - if ( function_exists('getrusage') ) // Funktion existiert auf WIN32 nicht. - { - foreach( getrusage() as $name=>$value ); - { - $treeElement = new TreeElement(); - $treeElement->text = $name.':'.$value; - $treeElement->description = lang('SETTING')." '".$name."'".(!empty($value)?': '.$value:''); - $treeElement->icon = 'config_property'; - $this->addTreeElement( $treeElement ); - } - } - } - - - - - function prefs_php( ) - { - $php_prefs = array( 'version' => phpversion(), - 'SAPI' => php_sapi_name(), - 'session-name' => session_name(), - 'magic_quotes_gpc' => get_magic_quotes_gpc(), - 'magic_quotes_runtime'=> get_magic_quotes_runtime() ); - - foreach( array('upload_max_filesize', - 'file_uploads', - 'memory_limit', - 'max_execution_time', - 'post_max_size', - 'display_errors', - 'register_globals' - ) as $iniName ) - $php_prefs[ $iniName ] = ini_get( $iniName ); - - foreach( $php_prefs as $key=>$value ) - { - $treeElement = new TreeElement(); - $treeElement->text = $key.'='.$value; - $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); - $treeElement->icon = 'config_property'; - $this->addTreeElement( $treeElement ); - } - } - - - - function prefs_extensions( ) - { - $extensions = get_loaded_extensions(); - asort( $extensions ); - - foreach( $extensions as $id=>$extensionName ) - { - $treeElement = new TreeElement(); - $treeElement->text = $extensionName; - $treeElement->icon = 'config_property'; - $treeElement->internalId = $id; - $this->addTreeElement( $treeElement ); - } - } - - - - function prefs_extension( $id ) - { - $extensions = get_loaded_extensions(); - $functions = get_extension_funcs( $extensions[$id] ); - asort( $functions ); - - foreach( $functions as $functionName ) - { - $treeElement = new TreeElement(); - $treeElement->text = $functionName; - $treeElement->icon = 'config_property'; - $this->addTreeElement( $treeElement ); - } - } - - - /** - * Anzeigen von Einstellungen. - * - * @param $id - */ - function prefs( ) - { - global $conf; - - if ( !@$conf['security']['show_system_info'] ) - return; - - $conf_config = $conf['interface']['config']; - - - $treeElement = new TreeElement(); - - $treeElement->internalId = -1; - $treeElement->text = 'OpenRat'; - $treeElement->icon = 'configuration'; - - if ( !empty($conf_config['file_manager_url']) ) - $treeElement->url = $conf_config['file_manager_url']; - $treeElement->target = '_blank'; - $treeElement->description = ''; - $treeElement->type = 'prefs_cms'; - $this->addTreeElement( $treeElement ); - - - - if ( !empty($conf_config['show_system']) ) - { - $treeElement = new TreeElement(); - - $treeElement->internalId = 0; - $treeElement->text = lang('GLOBAL_SYSTEM'); - $treeElement->icon = 'configuration'; - - $treeElement->description = ''; - $treeElement->target = 'cms_main'; - $treeElement->type = 'prefs_system'; - $this->addTreeElement( $treeElement ); - } - - - if ( !empty($conf_config['show_interpreter']) ) - { - $treeElement = new TreeElement(); - - $treeElement->internalId = 0; - $treeElement->text = lang('GLOBAL_PHP'); - $treeElement->icon = 'configuration'; - - $treeElement->description = ''; - $treeElement->target = 'cms_main'; - $treeElement->type = 'prefs_php'; - $this->addTreeElement( $treeElement ); - } - - - if ( !empty($conf_config['show_extensions']) ) - { - $treeElement = new TreeElement(); - - $treeElement->internalId = 0; - $treeElement->text = lang('GLOBAL_EXTENSIONS'); - $treeElement->icon = 'configuration'; - - $treeElement->description = ''; - $treeElement->target = 'cms_main'; - $treeElement->type = 'prefs_extensions'; - $this->addTreeElement( $treeElement ); - } - } - - - function prefs_cms( $id ) - { - global $conf; - - if ( $id < 0 ) - { - $tmpConf = $conf; - } - else - $tmpConf = $this->confCache[$id]; - - if ( !is_array($tmpConf) ) - $tmpConf = array('unknown'); - - foreach( $tmpConf as $key=>$value ) - { - if ( is_array($value) ) - { - $this->confCache[crc32($key)] = $value; - - $treeElement = new TreeElement(); - - $treeElement->internalId = crc32($key); - $treeElement->text = $key; -// if ( $id == 0 ) -// $treeElement->url = Html::url('main','prefs',0,array('conf'=>$key)); - $treeElement->icon = 'configuration'; - - $treeElement->description = count($value).' '.lang('SETTINGS'); - $treeElement->target = 'cms_main'; - $treeElement->type = 'prefs_cms'; - $this->addTreeElement( $treeElement ); - } - else - { - // Die PHP-funktion 'parse_ini_file()' liefert alle Einstellungen leider nur als String - // Daher weiß man hier nicht, ob '1' nun '1' oder 'true' heißen soll. - if ( $value=='' ) - // Anzeige 'Leer' - $value = lang('EMPTY'); - elseif ( $value=='0' ) - // Anzeige 'Nein' - $value = $value.' ('.lang('IS_NO').')'; - elseif ( $value=='1' ) - // Anzeige 'Ja' - $value = '+'.$value.' ('.lang('IS_YES').')'; - elseif ( is_numeric($value) ) - // Anzeige numerische Werte - $value = ($value>0?'+':'').$value; - else - // Anzeige von Zeichenketten - $value = $value; - - $this->confCache[crc32($key)] = $value; - - if ( strpos($key,'pass') !== FALSE ) - $value = '***'; // Kennwörter nicht anzeigen - - $treeElement = new TreeElement(); - $treeElement->text = $key.': '.$value; - $treeElement->icon = 'config_property'; - $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:''); - - $this->addTreeElement( $treeElement ); - } - } - } - - - function users( ) - { - foreach( User::getAllUsers() as $user ) - { - $treeElement = new TreeElement(); - $treeElement->id = $user->userid; - $treeElement->internalId = $user->userid; - $treeElement->text = $user->name; - $treeElement->url = Html::url('user','edit', - $user->userid,array(REQ_PARAM_TARGET=>'content') ); - $treeElement->action = 'user'; - $treeElement->icon = 'user'; - - $desc = $user->fullname; - - if ( $user->isAdmin ) - $desc .= ' ('.lang('USER_ADMIN').') '; - if ( $user->desc == "" ) - $desc .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - else - $desc .= ' - '.$user->desc; - - $treeElement->description = $desc; - $treeElement->target = 'cms_main'; - - $this->addTreeElement( $treeElement ); - } - } - - - function groups( ) - { - - foreach( Group::getAll() as $id=>$name ) - { - $treeElement = new TreeElement(); - - $g = new Group( $id ); - $g->load(); - - $treeElement->id = $id; - $treeElement->internalId = $id; - $treeElement->text = $g->name; - $treeElement->url = Html::url('group','edit',$id, - array(REQ_PARAM_TARGET=>'content') ); - $treeElement->icon = 'group'; - $treeElement->description = lang('GLOBAL_GROUP').' '.$g->name.': '.implode(', ',$g->getUsers()); - $treeElement->target = 'cms_main'; - $treeElement->type = 'userofgroup'; - $treeElement->action = 'group'; - - $this->addTreeElement( $treeElement ); - } - } - - - function userofgroup( $id ) - { - $g = new Group( $id ); - - foreach( $g->getUsers() as $id=>$name ) - { - $treeElement = new TreeElement(); - - $u = new User( $id ); - $u->load(); - $treeElement->id = $u->userid; - $treeElement->text = $u->name; - $treeElement->url = Html::url('user','edit',$id,array(REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'user'; - $treeElement->action = 'user'; - $treeElement->description = $u->fullname; - $treeElement->target = 'cms_main'; - - $this->addTreeElement( $treeElement ); - } - } -} - -?>- \ No newline at end of file diff --git a/util/Api.class.php b/util/Api.class.php @@ -1,101 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -use cms\model\Folder; - - -/** - * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere - * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen - * werden. - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Api -{ - var $output = ''; - var $objectid = 0; - var $page; - - function db() - { - return db_connection(); - } - - function pageid() - { - echo 'WARNING: pageid() deprecated!<br>'; - global $SESS; - return $SESS['objectid']; - } - - function getObjectId() - { - return $this->objectid; - } - - function setObjectId( $objectid ) - { - $this->objectid = $objectid; - } - - function getRootObjectId() - { - return Folder::getRootObjectId(); - } - - function folderid() - { - global $SESS; - return $SESS['folderid']; - } - - - function execute( $code ) - { - global $conf_tmpdir; - $code = "<?php\n".$code."\n?>"; - - $tmp = $conf_tmpdir.'/'.md5(microtime()).'.tmp'; - $f = fopen( $tmp,'w' ); - fwrite( $f,$code ); - fclose( $f ); - - require( $tmp ); // Ausfuehren des temporaeren PHP-Codes - - unlink( $tmp ); - $inhalt = Api::getOutput(); - $this->output( $inhalt ); - } - - function delOutput() - { - $this->output = ''; - } - - function output( $text ) - { - $this->output .= $text; - } - - - function getOutput() - { - return $this->output; - } -}- \ No newline at end of file diff --git a/util/ArchiveTar.class.php b/util/ArchiveTar.class.php @@ -1,397 +0,0 @@ -<?php -/* -======================================================================= -Name: - tar Class - -Author: - Josh Barger <joshb@npt.com> - -Description: - This class reads and writes Tape-Archive (TAR) Files and Gzip - compressed TAR files, which are mainly used on UNIX systems. - This class works on both windows AND unix systems, and does - NOT rely on external applications!! Woohoo! - -Usage: - Copyright (C) 2002 Josh Barger - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details at: - http://www.gnu.org/copyleft/lesser.html - - If you use this script in your application/website, please - send me an e-mail letting me know about it :) - -Bugs: - Please report any bugs you might find to my e-mail address - at joshb@npt.com. If you have already created a fix/patch - for the bug, please do send it to me so I can incorporate it into my release. - -Version History: - 1.0 04/10/2002 - InitialRelease - - 2.0 04/11/2002 - Merged both tarReader and tarWriter - classes into one - - Added support for gzipped tar files - Remember to name for .tar.gz or .tgz - if you use gzip compression! - :: THIS REQUIRES ZLIB EXTENSION :: - - Added additional comments to - functions to help users - - Added ability to remove files and - directories from archive - 2.1 04/12/2002 - Fixed serious bug in generating tar - - Created another example file - - Added check to make sure ZLIB is - installed before running GZIP - compression on TAR - 2.2 05/07/2002 - Added automatic detection of Gzipped - tar files (Thanks go to J�rgen Falch - for the idea) - - Changed "private" functions to have - special function names beginning with - two underscores -======================================================================= -*/ - - -class ArchiveTar -{ - // Unprocessed Archive Information - var $filename; - var $isGzipped; - var $tar_file; - - // Processed Archive Information - var $files; - var $directories; - var $numFiles; - var $numDirectories; - - - // Class Constructor -- Does nothing... - function tar() { - return true; - } - - - // Computes the unsigned Checksum of a file's header - // to try to ensure valid file - // PRIVATE ACCESS FUNCTION - function __computeUnsignedChecksum($bytestring) - { - $unsigned_chksum=0; - for($i=0; $i<512; $i++) - $unsigned_chksum += ord($bytestring[$i]); - for($i=0; $i<8; $i++) - $unsigned_chksum -= ord($bytestring[148 + $i]); - $unsigned_chksum += ord(" ") * 8; - - return $unsigned_chksum; - } - - - // Converts a NULL padded string to a non-NULL padded string - // PRIVATE ACCESS FUNCTION - function __parseNullPaddedString($string) - { - $position = strpos($string,chr(0)); - return substr($string,0,$position); - } - - - // This function parses the current TAR file - // PRIVATE ACCESS FUNCTION - function __parseTar() - { - // Read Files from archive - $this->numFiles=0; - $tar_length = strlen($this->tar_file); - $main_offset = 0; - while($main_offset < $tar_length) { - // If we read a block of 512 nulls, we are at the end of the archive - if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512)) - break; - - // Parse file name - $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100)); - - // Parse the file mode - $file_mode = substr($this->tar_file,$main_offset + 100,8); - - // Parse the file user ID - $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8)); - - // Parse the file group ID - $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8)); - - // Parse the file size - $file_size = octdec(substr($this->tar_file,$main_offset + 124,12)); - - // Parse the file update time - unix timestamp format - $file_time = octdec(substr($this->tar_file,$main_offset + 136,12)); - - // Parse Checksum - $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6)); - - // Parse user name - $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32)); - - // Parse Group name - $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32)); - - // Make sure our file is valid - if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum) - return false; - - // Parse File Contents - $file_contents = substr($this->tar_file,$main_offset + 512,$file_size); - - /* ### Unused Header Information ### - $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1); - $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100); - $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6); - $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2); - $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8); - $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8); - $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155); - $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12); - */ - - if($file_size > 0) { - // Increment number of files - $this->numFiles++; - - // Create us a new file in our array - $activeFile = &$this->files[]; - - // Asign Values - $activeFile["name"] = $file_name; - $activeFile["mode"] = $file_mode; - $activeFile["size"] = $file_size; - $activeFile["time"] = $file_time; - $activeFile["user_id"] = $file_uid; - $activeFile["group_id"] = $file_gid; - $activeFile["user_name"] = $file_uname; - $activeFile["group_name"] = $file_gname; - $activeFile["checksum"] = $file_chksum; - $activeFile["file"] = $file_contents; - - } else { - // Increment number of directories - $this->numDirectories++; - - // Create a new directory in our array - $activeDir = &$this->directories[]; - - // Assign values - $activeDir["name"] = $file_name; - $activeDir["mode"] = $file_mode; - $activeDir["time"] = $file_time; - $activeDir["user_id"] = $file_uid; - $activeDir["group_id"] = $file_gid; - $activeDir["user_name"] = $file_uname; - $activeDir["group_name"] = $file_gname; - $activeDir["checksum"] = $file_chksum; - } - - // Move our offset the number of blocks we have processed - $main_offset += 512 + (ceil($file_size / 512) * 512); - } - - return true; - } - - - // Read a non gzipped tar file in for processing - // PRIVATE ACCESS FUNCTION - function __readTar($filename='') - { - // Set the filename to load - // Read in the TAR file - - if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) { - if(!function_exists("gzinflate")) - return false; - - $this->isGzipped = TRUE; - - $this->tar_file = gzinflate(substr($this->tar_file,10,-4)); - } - - // Parse the TAR file - $this->__parseTar(); - - return true; - } - - - // Generates a TAR file from the processed data - // PRIVATE ACCESS FUNCTION - function __generateTAR() - { - // Clear any data currently in $this->tar_file - unset($this->tar_file); - - // Generate Records for each directory, if we have directories - if($this->numDirectories > 0) { - foreach($this->directories as $key => $information) { - unset($header); - - // Generate tar header for this directory - // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end - $header .= str_pad($information["name"],100,chr(0)); - $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_repeat(" ",8); - $header .= "5"; - $header .= str_repeat(chr(0),100); - $header .= str_pad("ustar",6,chr(32)); - $header .= chr(32) . chr(0); - $header .= str_pad("",32,chr(0)); - $header .= str_pad("",32,chr(0)); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),155); - $header .= str_repeat(chr(0),12); - - // Compute header checksum - $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); - for($i=0; $i<6; $i++) { - $header[(148 + $i)] = substr($checksum,$i,1); - } - $header[154] = chr(0); - $header[155] = chr(32); - - // Add new tar formatted data to tar file contents - $this->tar_file .= $header; - } - } - - // Generate Records for each file, if we have files (We should...) - if($this->numFiles > 0) - { - foreach($this->files as $key => $information) - { - unset($header); - $header = ''; - - // Generate the TAR header for this file - // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end - $header .= str_pad($information["name"],100,chr(0)); - $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0); - $header .= str_repeat(" ",8); - $header .= "0"; - $header .= str_repeat(chr(0),100); - $header .= str_pad("ustar",6,chr(32)); - $header .= chr(32) . chr(0); - $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP? - $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP? - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),8); - $header .= str_repeat(chr(0),155); - $header .= str_repeat(chr(0),12); - - // Compute header checksum - $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT); - for($i=0; $i<6; $i++) - { - $header[(148 + $i)] = substr($checksum,$i,1); - } - $header[154] = chr(0); - $header[155] = chr(32); - - // Pad file contents to byte count divisible by 512 - $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0)); - - // Add new tar formatted data to tar file contents - $this->tar_file .= $header . $file_contents; - } - } - - // Add 512 bytes of NULLs to designate EOF - $this->tar_file .= str_repeat(chr(0),512); - - return true; - } - - - // Open a TAR file - function openTAR($value) - { - // Clear any values from previous tar archives - unset($this->filename); - unset($this->isGzipped); - unset($this->tar_file); - unset($this->files); - unset($this->directories); - unset($this->numFiles); - unset($this->numDirectories); - - $this->filename = 'none'; - $this->tar_file = $value; - // Parse this file - $this->__readTar(); - - return true; - } - - - // Write the currently loaded tar archive to disk - function saveTar() - { - if(!$this->filename) - return false; - - // Write tar to current file using specified gzip compression - $this->toTar($this->filename,$this->isGzipped); - - return true; - } - - - // Saves tar archive to a different file than the current file - function toTar($filename,$useGzip) - { - if(!$filename) - return false; - - // Encode processed files into TAR file format - $this->__generateTar(); - - // GZ Compress the data if we need to - if($useGzip) { - // Make sure we have gzip support - if(!function_exists("gzencode")) - return false; - - $file = gzencode($this->tar_file); - } else { - $file = $this->tar_file; - } - - // Write the TAR file - $fp = fopen($filename,"wb"); - fwrite($fp,$file); - fclose($fp); - - return true; - } -} - -?>- \ No newline at end of file diff --git a/util/ArchiveUnzip.class.php b/util/ArchiveUnzip.class.php @@ -1,447 +0,0 @@ -<?php -// 28/11/2005 (2.4) -// - dUnzip2 is now compliant with wrong placed "Data Description", made by some compressors, -// like the classes ZipLib and ZipLib2 by 'Hasin Hayder'. Thanks to Ricardo Parreno for pointing it. -// 09/11/2005 (2.3) -// - Added optional parameter '$stopOnFile' on method 'getList()'. -// If given, file listing will stop when find given filename. (Useful to open and unzip an exact file) -// 06/11/2005 (2.21) -// - Added support to PK00 file format (Packed to Removable Disk) (thanks to Lito [PHPfileNavigator]) -// - Method 'getExtraInfo': If requested file doesn't exist, return FALSE instead of Array() -// 31/10/2005 (2.2) -// - Removed redundant 'file_name' on centralDirs declaration (thanks to Lito [PHPfileNavigator]) -// - Fixed redeclaration of file_put_contents when in PHP4 (not returning true) - -############################################################## -# Class dUnzip2 v2.4 -# -# Author: Alexandre Tedeschi (d) -# E-Mail: alexandrebr at gmail dot com -# Londrina - PR / Brazil -# -# Objective: -# This class allows programmer to easily unzip files on the fly. -# -# Requirements: -# This class requires extension ZLib Enabled. It is default -# for most site hosts around the world, and for the PHP Win32 dist. -# -# To do: -# * Error handling -# * Write a PHP-Side gzinflate, to completely avoid any external extensions -# * Write other decompress algorithms -# -# If you modify this class, or have any ideas to improve it, please contact me! -# You are allowed to redistribute this class, if you keep my name and contact e-mail on it. -############################################################## - -class ArchiveUnzip{ - - // Public - var $files = array(); - var $value = ''; - var $fileName; - var $compressedList; // You will problably use only this one! - var $centralDirList; // Central dir list... It's a kind of 'extra attributes' for a set of files - var $endOfCentral; // End of central dir, contains ZIP Comments - var $debug; - - // Private - var $fh; - var $zipSignature = "\x50\x4b\x03\x04"; // local file header signature - var $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature - var $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature - - // Public - Function ArchiveUnzip() - { - $this->compressedList = - $this->centralDirList = - $this->endOfCentral = Array(); - } - - function open( $value ) - { - $this->fileName = tempnam('/tmp','unzip'); -// echo $this->fileName; - $fo = fopen( $this->fileName,'w'); - fwrite($fo,$value); - $this->unzipAll(); - } - - - Function getList($stopOnFile=false){ - if(sizeof($this->compressedList)){ - $this->debugMsg(1, "Returning already loaded file list."); - return $this->compressedList; - } - - // Open file, and set file handler - $fh = fopen($this->fileName, "r"); - $this->fh = &$fh; - if(!$fh){ - $this->debugMsg(2, "Failed to load file."); - return false; - } - - // Loop the file, looking for files and folders - $ddTry = false; - fseek($fh, 0); - for(;;){ - // Check if the signature is valid... - $signature = fread($fh, 4); - if(feof($fh)){ -# $this->debugMsg(1, "Reached end of file"); - break; - } - - // If signature is a 'Packed to Removable Disk', just ignore it and move to the next. - if($signature == 'PK00'){ - $this->debugMsg(1, "Found PK00: Packed to Removable Disk"); - continue; - } - - // If signature of a 'Local File Header' - if($signature == $this->zipSignature){ - # $this->debugMsg(1, "Zip Signature!"); - - // Get information about the zipped file - $file['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract - $file['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag - $file['compression_method'] = unpack("v", fread($fh, 2)); // compression method - $file['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time - $file['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date - $file['crc-32'] = fread($fh, 4); // crc-32 - $file['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size - $file['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size - $fileNameLength = unpack("v", fread($fh, 2)); // filename length - $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length - $file['file_name'] = fread($fh, $fileNameLength[1]); // filename - $file['extra_field'] = $extraFieldLength[1]?fread($fh, $extraFieldLength[1]):''; // extra field - $file['contents-startOffset']= ftell($fh); - - // Bypass the whole compressed contents, and look for the next file - fseek($fh, $file['compressed_size'][1], SEEK_CUR); - - // Convert the date and time, from MS-DOS format to UNIX Timestamp - $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT); - $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT); - $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; - $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); - $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); - $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); - $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); - $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)); - - // Mount file table - $this->compressedList[$file['file_name']] = Array( - 'file_name' =>$file['file_name'], - 'compression_method'=>$file['compression_method'][1], - 'version_needed' =>$file['version_needed'][1], - 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY), - 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), - 'compressed_size' =>$file['compressed_size'][1], - 'uncompressed_size' =>$file['uncompressed_size'][1], - 'extra_field' =>$file['extra_field'], - 'general_bit_flag' =>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), - 'contents-startOffset'=>$file['contents-startOffset'] - ); - - if($stopOnFile) if($file['file_name'] == $stopOnFile){ - $this->debugMsg(1, "Stopping on file..."); - break; - } - } - - // If signature of a 'Central Directory Structure' - elseif($signature == $this->dirSignature){ - # $this->debugMsg(1, "Dir Signature!"); - - $dir['version_madeby'] = unpack("v", fread($fh, 2)); // version made by - $dir['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract - $dir['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag - $dir['compression_method'] = unpack("v", fread($fh, 2)); // compression method - $dir['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time - $dir['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date - $dir['crc-32'] = fread($fh, 4); // crc-32 - $dir['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size - $dir['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size - $fileNameLength = unpack("v", fread($fh, 2)); // filename length - $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length - $fileCommentLength = unpack("v", fread($fh, 2)); // file comment length - $dir['disk_number_start'] = unpack("v", fread($fh, 2)); // disk number start - $dir['internal_attributes'] = unpack("v", fread($fh, 2)); // internal file attributes-byte1 - $dir['external_attributes1']= unpack("v", fread($fh, 2)); // external file attributes-byte2 - $dir['external_attributes2']= unpack("v", fread($fh, 2)); // external file attributes - $dir['relative_offset'] = unpack("V", fread($fh, 4)); // relative offset of local header - $dir['file_name'] = fread($fh, $fileNameLength[1]); // filename - $dir['extra_field'] = $extraFieldLength[1] ?fread($fh, $extraFieldLength[1]) :''; // extra field - $dir['file_comment'] = $fileCommentLength[1]?fread($fh, $fileCommentLength[1]):''; // file comment - - // Convert the date and time, from MS-DOS format to UNIX Timestamp - $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT); - $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT); - $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980; - $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4)); - $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5)); - $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5)); - $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6)); - $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)); - - $this->centralDirList[$dir['file_name']] = Array( - 'version_madeby'=>$dir['version_madeby'][1], - 'version_needed'=>$dir['version_needed'][1], - 'general_bit_flag'=>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT), - 'compression_method'=>$dir['compression_method'][1], - 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY), - 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT). - str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT), - 'compressed_size'=>$dir['compressed_size'][1], - 'uncompressed_size'=>$dir['uncompressed_size'][1], - 'disk_number_start'=>$dir['disk_number_start'][1], - 'internal_attributes'=>$dir['internal_attributes'][1], - 'external_attributes1'=>$dir['external_attributes1'][1], - 'external_attributes2'=>$dir['external_attributes2'][1], - 'relative_offset'=>$dir['relative_offset'][1], - 'file_name'=>$dir['file_name'], - 'extra_field'=>$dir['extra_field'], - 'file_comment'=>$dir['file_comment'], - ); - } - - elseif($signature == $this->dirSignatureE){ - # $this->debugMsg(1, "EOF Dir Signature!"); - - $eodir['disk_number_this'] = unpack("v", fread($fh, 2)); // number of this disk - $eodir['disk_number'] = unpack("v", fread($fh, 2)); // number of the disk with the start of the central directory - $eodir['total_entries_this'] = unpack("v", fread($fh, 2)); // total number of entries in the central dir on this disk - $eodir['total_entries'] = unpack("v", fread($fh, 2)); // total number of entries in - $eodir['size_of_cd'] = unpack("V", fread($fh, 4)); // size of the central directory - $eodir['offset_start_cd'] = unpack("V", fread($fh, 4)); // offset of start of central directory with respect to the starting disk number - $zipFileCommentLenght = unpack("v", fread($fh, 2)); // zipfile comment length - $eodir['zipfile_comment'] = $zipFileCommentLenght[1]?fread($fh, $zipFileCommentLenght[1]):''; // zipfile comment - $this->endOfCentral = Array( - 'disk_number_this'=>$eodir['disk_number_this'][1], - 'disk_number'=>$eodir['disk_number'][1], - 'total_entries_this'=>$eodir['total_entries_this'][1], - 'total_entries'=>$eodir['total_entries'][1], - 'size_of_cd'=>$eodir['size_of_cd'][1], - 'offset_start_cd'=>$eodir['offset_start_cd'][1], - 'zipfile_comment'=>$eodir['zipfile_comment'], - ); - } - else{ - if(!$ddTry){ - $this->debugMsg(1, "Unexpected header. Trying to detect wrong placed 'Data Descriptor'...\n"); - $ddTry = true; - fseek($fh, 12-4, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4) - continue; - } - $this->debugMsg(1, "Unexpected header, ending loop at offset ".ftell($fh)); - break; - } - $ddTry = false; - } - - if($this->debug){ - #------- Debug compressedList - $kkk = 0; - echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>"; - foreach($this->compressedList as $fileName=>$item){ - if(!$kkk && $kkk=1){ - echo "<tr style='background: #ADA'>"; - foreach($item as $fieldName=>$value) - echo "<td>$fieldName</td>"; - echo '</tr>'; - } - echo "<tr style='background: #CFC'>"; - foreach($item as $fieldName=>$value){ - if($fieldName == 'lastmod_datetime') - echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>"; - else - echo "<td title='$fieldName' nowrap='nowrap'>$value</td>"; - } - echo "</tr>"; - } - echo "</table>"; - - #------- Debug centralDirList - $kkk = 0; - if(sizeof($this->centralDirList)){ - echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>"; - foreach($this->centralDirList as $fileName=>$item){ - if(!$kkk && $kkk=1){ - echo "<tr style='background: #AAD'>"; - foreach($item as $fieldName=>$value) - echo "<td>$fieldName</td>"; - echo '</tr>'; - } - echo "<tr style='background: #CCF'>"; - foreach($item as $fieldName=>$value){ - if($fieldName == 'lastmod_datetime') - echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>"; - else - echo "<td title='$fieldName' nowrap='nowrap'>$value</td>"; - } - echo "</tr>"; - } - echo "</table>"; - } - - #------- Debug endOfCentral - $kkk = 0; - if(sizeof($this->endOfCentral)){ - echo "<table border='0' style='font: 11px Verdana' style='border: 1px solid #000'>"; - echo "<tr style='background: #DAA'><td colspan='2'>dUnzip - End of file</td></tr>"; - foreach($this->endOfCentral as $field=>$value){ - echo "<tr>"; - echo "<td style='background: #FCC'>$field</td>"; - echo "<td style='background: #FDD'>$value</td>"; - echo "</tr>"; - } - echo "</table>"; - } - } - - return $this->compressedList; - } - - - Function getExtraInfo($compressedFileName) - { - return - isset($this->centralDirList[$compressedFileName])? - $this->centralDirList[$compressedFileName]: - false; - } - - - Function getZipInfo($detail=false) - { - return $detail? - $this->endOfCentral[$detail]: - $this->endOfCentral; - } - - - Function unzip($compressedFileName, $targetFileName=false){ - $fdetails = &$this->compressedList[$compressedFileName]; - - if(!sizeof($this->compressedList)){ - $this->debugMsg(1, "Trying to unzip before loading file list... Loading it!"); - $this->getList(false, $compressedFileName); - } - if(!isset($this->compressedList[$compressedFileName])){ - $this->debugMsg(2, "File '<b>$compressedFileName</b>' is not compressed in the zip."); - return false; - } - if(substr($compressedFileName, -1) == "/"){ - $this->debugMsg(2, "Trying to unzip a folder name '<b>$compressedFileName</b>'."); - return false; - } - if(!$fdetails['uncompressed_size']){ - $this->debugMsg(1, "File '<b>$compressedFileName</b>' is empty."); - return ""; - } - - fseek($this->fh, $fdetails['contents-startOffset']); - return $this->uncompress( - fread($this->fh, $fdetails['compressed_size']), - $fdetails['compression_method'], - $fdetails['uncompressed_size'] ); - } - - - Function unzipAll($targetDir=false, $baseDir="", $maintainStructure=true, $chmod=false){ - if($targetDir === false) - $targetDir = dirname(__FILE__)."/"; - - $lista = $this->getList(); - if(sizeof($lista)) foreach($lista as $fileName=>$trash){ - $dirname = dirname($fileName); - $outDN = "$targetDir/$dirname"; - - if(substr($dirname, 0, strlen($baseDir)) != $baseDir) - continue; - - if(!is_dir($outDN) && $maintainStructure){ - $str = ""; - $folders = explode("/", $dirname); - foreach($folders as $folder){ - $str = $str?"$str/$folder":$folder; - if(!is_dir("$targetDir/$str")){ - $this->debugMsg(1, "Creating folder: $targetDir/$str"); - mkdir("$targetDir/$str"); - if($chmod) - chmod("$targetDir/$str", $chmod); - } - } - } - if(substr($fileName, -1, 1) == "/") - continue; - - $maintainStructure? - $this->unzip($fileName, "$targetDir/$fileName"): - $this->unzip($fileName, "$targetDir/".basename($fileName)); - - if($chmod) - chmod($maintainStructure?"$targetDir/$fileName":"$targetDir/".basename($fileName), $chmod); - } - } - - Function close(){ // Free the file resource - if($this->fh) - fclose($this->fh); - } - - // Private (you should NOT call these methods): - Function uncompress($content, $mode, $uncompressedSize, $targetFileName=false){ - switch($mode){ - case 0: - // Not compressed - return $content; - case 1: - $this->debugMsg(2, "Shrunk mode is not supported... yet?"); - return false; - case 2: - case 3: - case 4: - case 5: - $this->debugMsg(2, "Compression factor ".($mode-1)." is not supported... yet?"); - return false; - case 6: - $this->debugMsg(2, "Implode is not supported... yet?"); - return false; - case 7: - $this->debugMsg(2, "Tokenizing compression algorithm is not supported... yet?"); - return false; - case 8: - // Deflate - return gzinflate($content, $uncompressedSize); - case 9: - $this->debugMsg(2, "Enhanced Deflating is not supported... yet?"); - return false; - case 10: - $this->debugMsg(2, "PKWARE Date Compression Library Impoloding is not supported... yet?"); - return false; - default: - $this->debugMsg(2, "Unknown uncompress method: $mode"); - return false; - } - } - - - Function debugMsg($level, $string){ - if($this->debug) - if($level == 1) - echo "<b style='color: #777'>dUnzip2:</b> $string<br>"; - if($level == 2) - echo "<b style='color: #F00'>dUnzip2:</b> $string<br>"; - } -} -?>- \ No newline at end of file diff --git a/util/ArchiveZip.class.php b/util/ArchiveZip.class.php @@ -1,89 +0,0 @@ -<?php - - -/** - * This source is taken from http://www.zend.com/zend/spotlight/creating-zip-files1.php - * Thank you! - */ -class ArchiveZip -{ - var $datasec = array(); - var $ctrl_dir = array(); - var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; - var $old_offset = 0; - - - function add_file($data, $name) - { - $name = str_replace("\\", "/", $name); - $unc_len = strlen($data); - $crc = crc32($data); - $zdata = gzcompress($data); - $zdate = substr ($zdata, 2, -4); - $c_len = strlen($zdata); - - - - $fr = "\x50\x4b\x03\x04"; - $fr .= "\x14\x00"; - $fr .= "\x00\x00"; - $fr .= "\x08\x00"; - $fr .= "\x00\x00\x00\x00"; - $fr .= pack("V",$crc); - $fr .= pack("V",$c_len); - $fr .= pack("V",$unc_len); - $fr .= pack("v", strlen($name) ); - $fr .= pack("v", 0 ); - $fr .= $name; - $fr .= $zdata; - $fr .= pack("V",$crc); - $fr .= pack("V",$c_len); - $fr .= pack("V",$unc_len); - - $this -> datasec[] = $fr; - - - - $new_offset = strlen(implode("", $this->datasec)); - - $cdrec = "\x50\x4b\x01\x02"; - $cdrec .="\x00\x00"; - $cdrec .="\x14\x00"; - $cdrec .="\x00\x00"; - $cdrec .="\x08\x00"; - $cdrec .="\x00\x00\x00\x00"; - $cdrec .= pack("V",$crc); - $cdrec .= pack("V",$c_len); - $cdrec .= pack("V",$unc_len); - $cdrec .= pack("v", strlen($name) ); - $cdrec .= pack("v", 0 ); - $cdrec .= pack("v", 0 ); - $cdrec .= pack("v", 0 ); - $cdrec .= pack("v", 0 ); - $cdrec .= pack("V", 32 ); - $cdrec .= pack("V", $this -> old_offset ); - - $this -> old_offset = $new_offset; - - $cdrec .= $name; - $this -> ctrl_dir[] = $cdrec; - } - - - function file() { - $data = implode("", $this -> datasec); - $ctrldir = implode("", $this -> ctrl_dir); - - return - $data. - $ctrldir. - $this -> eof_ctrl_dir. - pack("v", sizeof($this -> ctrl_dir)). - pack("v", sizeof($this -> ctrl_dir)). - pack("V", strlen($ctrldir)). - pack("V", strlen($data)). - "\x00\x00"; - } -} - -?>- \ No newline at end of file diff --git a/util/Code.class.php b/util/Code.class.php @@ -1,44 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Code extends Macro -{ - var $code = ''; - - function execute() - { - if ( substr($this->code,0,5) != '<?php' ) - $this->code = "<?php\n".$this->code."\n?>"; - - $tmp = FileUtils::getTempDir().'/openratMacro'; - $tmp .= '.code.php.tmp'; - - $f = fopen( $tmp,'w' ); - fwrite( $f,$this->code ); - fclose( $f ); - - require( $tmp ); // Ausfuehren des temporaeren PHP-Codes - - unlink( $tmp ); - } -}- \ No newline at end of file diff --git a/util/Dynamic.class.php b/util/Dynamic.class.php @@ -1,34 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2013 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Oberklasse für dynamische Klassen ("Makros"). - * - * Diese Klasse dient nur der Abwärtskompabilität. Neue dynamische Klassen - * (ab jetzt "Makros") sollten von der Klasse Macro erben. - * - * @author Jan Dankert - * @package openrat.services - */ -class Dynamic extends Macro -{ - // Keine weitere Implementierung -} - -?>- \ No newline at end of file diff --git a/util/FileUtils.class.php b/util/FileUtils.class.php @@ -1,102 +0,0 @@ -<?php - -/** - * Werkzeugklasse f�r Datei-Operationen. - * - */ -class FileUtils -{ - /** - * Fuegt einen Slash ("/") an das Ende an, sofern nicht bereits vorhanden. - * - * @param String $pfad - * @return Pfad mit angeh�ngtem Slash. - */ - public static function slashify($pfad) - { - if ( substr($pfad,-1,1) == '/') - return $pfad; - else - return $pfad.'/'; - } - - - - /** - * Liefert einen Verzeichnisnamen fuer temporaere Dateien. - */ - public static function createTempFile() - { - global $conf; - $tmpdir = @$conf['cache']['tmp_dir']; - $tmpfile = @tempnam( $tmpdir,'openrat_tmp' ); - - // 2. Versuch: Temp-Dir aus "upload_tmp_dir". - if ( $tmpfile === FALSE ) - { - $tmpdir = ini_get('upload_tmp_dir'); - $tmpfile = @tempnam( $tmpdir,'openrat_tmp' ); - } - - elseif ( $tmpfile === FALSE ) - { - $tmpfile = @tempnam( '','openrat_tmp' ); - } - - return $tmpfile; - } - - - /** - * Liefert einen Verzeichnisnamen fuer temporaere Dateien. - */ - public static function getTempDir() - { - $tmpfile = FileUtils::createTempFile(); - - $tmpdir = dirname($tmpfile); - @unlink($tmpfile); - - return FileUtils::slashify( $tmpdir ); - } - - - - /** - * Liest die Dateien aus dem angegebenen Ordner in ein Array. - * - * @param $dir Verzeichnis, welches gelesen werden soll - * @return Array Liste der Dateien im Ordner - */ - public static function readDir($dir) - { - $dir = FileUtils::slashify($dir); - $dateien = array(); - - if ( !is_dir($dir) ) - { - return false; - } - - if ( $dh = opendir($dir) ) - { - while( ($verzEintrag = readdir($dh)) !== false ) - { - if ( substr($verzEintrag,0,1) != '.' ) - { - $dateien[] = $verzEintrag; - } - } - closedir($dh); - - return $dateien; - } - else - { - die('unable to open directory: '.$dir); - } - - } -} - -?>- \ No newline at end of file diff --git a/util/Ftp.class.php b/util/Ftp.class.php @@ -1,246 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Darstellen einer FTP-Verbindung, das beinhaltet - * das Login, das Kopieren von Dateien sowie praktische - * FTP-Funktionen - * - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Ftp -{ - var $verb; - var $url; - var $log = array(); - - var $passive = false; - - var $ok = true; - - private $path; - - - // Konstruktor - function __construct( $url ) - { - $this->connect( $url ); - } - - - // Aufbauen der Verbindung - function connect( $url ) - { - $this->url = $url; - - global $conf; - - $conf_ftp = $conf['publish']['ftp']; - $ftp = parse_url( $this->url ); - - // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten - $ftp = array_merge($conf_ftp,$ftp); - - // Nur FTP und FTPS (seit PHP 4.3) erlaubt - if ( !in_array(@$ftp['scheme'],array('ftp','ftps')) ) - { - $this->log[] = 'Unknown scheme in FTP Url: '.@$ftp['scheme']; - $this->log[] = 'Only FTP (and FTPS, if compiled in) are supported'; - $this->ok = false; - return; - } - - if ( function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps' ) - $this->verb = @ftp_ssl_connect( $ftp['host'],$ftp['port'] ); - else - $this->verb = @ftp_connect( $ftp['host'],$ftp['port'] ); - - if ( !$this->verb ) - { - $this->log[] = 'Cannot connect to '.$ftp['scheme'].'-server: '.$ftp['host'].':'.$ftp['port']; - $this->ok = false; - - Logger::error('Cannot connect to '.$ftp['host'].':'.$ftp['port']); - return; - } - - $this->log[] = 'Connected to FTP server '.$ftp['host'].':'.$ftp['port']; - - if ( empty($ftp['user']) ) - { - $ftp['user'] = 'anonymous'; - $ftp['pass'] = 'openrat@openrat.de'; - } - - if ( ! ftp_login( $this->verb,$ftp['user'],$ftp['pass'] ) ) - { - $this->log[] = 'Unable to login as user '.$ftp['user']; - $this->ok = false; - return; - } - - $this->log[] = 'Logged in as user '.$ftp['user']; - - $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive' ); - - $this->log[] = 'entering passive mode '.($pasv?'on':'off'); - if ( ! ftp_pasv($this->verb,true) ) - { - $this->log[] = 'cannot switch PASV mode'; - $this->ok = false; - return; - } - - if ( !empty($ftp['query']) ) - { - parse_str( $ftp['query'],$ftp_var ); - - if ( isset( $ftp_var['site'] ) ) - { - $site_commands = explode( ',',$ftp_var['site'] ); - foreach( $site_commands as $cmd ) - { - $this->log .= 'executing SITE command: '.$cmd; - - if ( ! @ftp_site( $this->verb,$cmd ) ) - { - $this->log[] = 'unable to do SITE command: '.$cmd; - $this->ok = false; - return; - } - } - } - } - - $this->path = rtrim( $ftp['path'],'/' ); - - $this->log[] = 'Changing directory to '.$this->path; - - if ( ! @ftp_chdir( $this->verb,$this->path ) ) - { - $this->log[] = 'unable CHDIR to directory: '.$this->path; - $this->ok = false; - return; - } - } - - - /** - * Kopieren einer Datei vom lokalen System auf den FTP-Server. - * - * @param String Quelle - * @param String Ziel - * @param int FTP-Mode (BINARY oder ASCII) - */ - function put( $source,$dest ) - { - if ( ! $this->ok ) - return; - - $ftp = parse_url( $this->url ); - - $dest = $this->path.'/'.$dest; - - $this->log .= "Copying file: $source -&gt; $dest ...\n"; - - $mode = FTP_BINARY; - $p = strrpos( basename($dest),'.' ); // Letzten Punkt suchen - - if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen - { - $extension = substr( basename($dest),$p+1 ); - $type = config('mime-types',$extension); - if ( substr($type,0,5) == 'text/') - $mode = FTP_ASCII; - } - - Logger::debug("FTP PUT target:$dest mode:".(($mode==FTP_ASCII)?'ascii':'binary')); - - if ( !@ftp_put( $this->verb,$dest,$source,$mode ) ) - { - if ( !$this->mkdirs( dirname($dest) ) ) - return; // Fehler. - - ftp_chdir( $this->verb,$this->path ); - - if ( ! @ftp_put( $this->verb,$dest,$source,$mode ) ) - { - $this->ok = false; - $this->log[] = 'FTP PUT failed...'; - $this->log[] = 'source : '.$source; - $this->log[] = 'destination: '.$dest; - return; - } - - } - } - - - - /** - * Private Methode zum rekursiven Anlegen von Verzeichnissen. - * - * @param String Pfad - * @return boolean true, wenn ok - */ - function mkdirs( $strPath ) - { - if ( @ftp_chdir($this->verb,$strPath) ) - return true; // Verzeichnis existiert schon :) - - $pStrPath = dirname($strPath); - - if ( !$this->mkdirs($pStrPath) ) - return false; - - if ( ! @ftp_mkdir($this->verb,$strPath) ) - { - $this->ok = false; - $this->log[] = "failed to create remote directory: $strPath"; - } - - return $this->ok; - } - - - - /** - * Schlie�en der FTP-Verbindung.<br> - * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben. - */ - function close() - { - if ( !$this->ok ) // Noch alles ok? - return; - - if ( ! @ftp_quit( $this->verb ) ) - { - // Das Schlie�en der Verbindung hat nicht funktioniert. - // Eigentlich k�nnten wir das ignorieren, aber wir sind anst�ndig und melden eine Fehler. - $this->log[] = 'failed to close connection'; - $this->ok = false; - return; - } - } -} - - -?>- \ No newline at end of file diff --git a/util/GlobalFunctions.class.php b/util/GlobalFunctions.class.php @@ -1,71 +0,0 @@ -<?php - -/** - * Bereitstellen von globalen Funktionen - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class GlobalFunctions -{ - public static function getIsoCodes() - { - global $conf_php; - - $iso = parse_ini_file( './language/lang.ini.'.$conf_php ); - asort( $iso ); - return $iso; - } - - - public static function lang( $text ) - { - global $SESS; - $text = strtoupper($text); - - if ( isset( $SESS['lang'][$text] ) ) - { - return $SESS['lang'][$text]; - } - else - { - return( '?'.$text.'?' ); - } - } - - - # Spracheinstellungen laden - - public static function language_from_http() - { - global $SESS, - $HTTP_SERVER_VARS, - $conf_php, - $conf; - - $languages = $HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE']; - $languages = explode(',',$languages); - foreach( $languages as $l ) - { - $l = substr($l,0,2); - if ( file_exists("./language/$l.ini.$conf_php") ) - return( $l ); - } - - // Keine passende Sprache im HTTP-Header gefunden - return $conf['global']['default_language']; - } - - - public static function language_read( $l='' ) - { - global $SESS, - $HTTP_SERVER_VARS, - $conf_php; - - $l = language_from_http(); - $SESS['lang'] = parse_ini_file( "./language/$l.ini.$conf_php" ); - } -} - -?>- \ No newline at end of file diff --git a/util/Html.class.php b/util/Html.class.php @@ -1,178 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Bereitstellen von Methoden fuer die Darstellung von HTML-Elementen - * - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Html -{ - public static function error( $field ) - { - global $inputErrors; - - if ( isset($inputErrors[$field]) ) - return '<span class="error">'.lang($inputErrors[$field]).'</span'; - } - - - - /** - * Ausgabe eines Variablenwertes.<br> - */ - public static function debug( $wert, $text='' ) - { - echo "<strong>DEBUG: $text (".gettype($wert).")</strong><br/>"; - echo "<pre>"; - print_r($wert); - echo "</pre>"; - } - - - /** - * Erzeugt eine relative Url innerhalb von Openrat - * - * @param string Aktion, die aufgerufen werden soll - * @param string Unteraktion, die innerhalb der Aktion aufgerufen werden soll - * @param int Id fuer diesen Aufruf - * @param array Weitere beliebige Parameter - */ - public static function url( $action,$subaction='',$id='',$params=array() ) - { - if ( intval($id)==0 ) - $id='-'; - - global $conf; - - if ( is_array($action) ) - { - $params = $action; - - if ( isset($params['callAction']) ) - { - $params['subaction'] = $params['callAction']; - unset( $params['callAction'] ); - unset( $params['callSubaction'] ); - } - - - if ( !isset($params['action' ])) $params['action' ] = ''; - if ( !isset($params['subaction'])) $params['subaction'] = ''; - if ( !isset($params['id' ])) $params['id' ] = ''; - $action = $params['action' ]; - $subaction = $params['subaction']; - $id = $params['id' ]; - unset( $params['action' ] ); - unset( $params['subaction'] ); - unset( $params['id' ] ); - $params['old']='true'; - } - - // Session-Id ergaenzen - if ( $conf['interface']['url']['add_sessionid'] ) - $params[ session_name() ] = session_id(); - - if ( config('security','use_post_token') ) - $params[ 'token'] = token(); - - $fake_urls = $conf['interface']['url']['fake_url' ]; - $url_format = $conf['interface']['url']['url_format']; - - if ( isset($params['objectid']) && !isset($params['id']) ) - $params['id'] = $params['objectid']; - - if ( $fake_urls ) - { -// if ( $id != '' ) -// $id = '.'.$id; - } - else - { - global $view; - $params[REQ_PARAM_ACTION ] = $action; - $params[REQ_PARAM_SUBACTION] = $subaction; - $params[REQ_PARAM_ID ] = $id; - - if ( !isset($params[REQ_PARAM_TARGET])) - $params[REQ_PARAM_TARGET ] = $view; - } - - if ( count($params) > 0 ) - { - $urlParameterList = array(); - foreach( $params as $var=>$value ) - { - $urlParameterList[] = urlencode($var).'='.urlencode($value); - } - $urlParameter = '?'.implode('&amp;',$urlParameterList); - } - else - { - $urlParameter = ''; - } - - if ( @$conf['interface']['url']['index'] ) - $controller_file_name = ''; - else - $controller_file_name = OR_CONTROLLER_FILE.'.'.PHP_EXT; - - $prefix = './'; - - if ( $fake_urls ) - $src = sprintf( $url_format,$action,$subaction,$id,session_id() ).$urlParameter; - else - $src = $prefix.$controller_file_name.$urlParameter; - - return $src; - } - - - - public static function complete_tag($tagname,$attributes) - { - $text = '<'.$tagname; - foreach( $attributes as $attribute_name=>$attribute_value ) - if ( !empty($attribute_value) ) - $text .= ' '.$attribute_name.'="'.$attribute_value.'"'; - $text .= ' />'; - return $text; - } - - - - public static function open_tag($tagname,$attributes) - { - $text = '<'.$tagname; - foreach( $attributes as $attribute_name=>$attribute_value ) - if ( !empty($attribute_value) ) - $text .= ' '.$attribute_name.'="'.$attribute_value.'"'; - $text .= '>'; - return $text; - } - - - public static function close_tag($tagname) - { - return '</'.$tagname.'>'; - } -} -?>- \ No newline at end of file diff --git a/util/Http.class.php b/util/Http.class.php @@ -1,610 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Kapselung einer HTTP-Anfrage.<br> - * Unter Beruecksichtigung von RFC 1945.<br> - * - * @author Jan Dankert - * @package openrat.services - */ -class Http -{ - public $header = array(); - - public $url = array(); - public $responseHeader = array(); - public $requestParameter = array(); - public $urlParameter = array(); - - /** - * HTTP-Request-Typ.<br> - * Muss entweder "GET" oder "POST" sein.<br> - * Default: "GET". - * - * @var String Request-Typ - */ - public $method = 'GET'; - public $error = ''; - public $status = ''; - public $body = ''; - - public $httpCmd = ''; - - - - /** - * Erzeugt eine HTTP-Anfrage. - * - * @param String URL - * @return Http - */ - public function Http( $url = '' ) - { - $this->setURL( $url ); - $this->header['User-Agent'] = 'Mozilla/5.0 (OpenRat CMS)'; - $this->header['Connection'] = 'close'; - } - - - - /** - * Setzt die URL. - * - * @param String URL - */ - function setURL( $url ) - { - $this->url = parse_url($url); - - if ( empty($this->url['host']) && !empty($this->url['path']) ) - { - $this->url['host'] = basename($this->url['path']); - $this->url['path'] = '/'; - } - - if ( empty($this->url['path']) ) - $this->url['path'] = '/'; - - if ( !isset($this->url['port']) ) - if ( !isset($this->url['scheme']) ) - { - $this->url['scheme'] = 'http'; // Standard-Port. - $this->url['port'] = 80; // Standard-Port. - } - elseif ( $this->url['scheme'] == 'https' ) - $this->url['port'] = 443; // SSL-Port. - else - $this->url['port'] = 80; // Standard-Port. - - if ( !empty($this->url['query']) ) - parse_str( $this->url['query'],$this->urlParameter ); - - } - - - - /** - * Setzt Authentisierungsinformationen in den HTTP-Request.<br> - * - * @param String Benutzername - * @param String Kennwort - */ - public function setBasicAuthentication( $user, $password ) - { - $this->header['Authorization'] = 'Basic '.base64_encode($user.':'.$password); - } - - - - /** - * Erzeugt eine HTTP-Parameterstring mit allen Parametern. - * - * @param withPraefixQuestionMark Praefix mit Fragezeichen (fuer GET-Anfragen) - * @return String URL-Parameter - */ - public function getParameterString( $withPraefixQuestionMark=false ) - { - $parameterString = ''; - $parameter = $this->urlParameter + $this->requestParameter; - - if ( ! empty($parameter) ) - { - foreach( $this->requestParameter as $paramName => $paramValue ) - { - if ( strlen($parameterString) > 0) - $parameterString .= '&'; - elseif ( $withPraefixQuestionMark ) - $parameterString .= '?'; - - $parameterString .= urlencode($paramName) . '=' .urlencode($paramValue); - } - } - - return $parameterString; - } - - - /** - * Liefert die URL des Requests. - * - * @return String URL - */ - public function getUrl() - { - $location = $this->url['scheme']; - $location .= '://'; - $location .= $this->url['host']; - if ( $this->url['scheme'] == 'http' && $this->url['port'] != 80 || - $this->url['scheme'] == 'https' && $this->url['port'] != 443 ) - $location .= ':'.$this->url['port']; - $location .= $this->url['path']; - - $location .= $this->getParameterString(true); - - if ( isset($this->url['fragment']) ) - $location .= '#'.$this->url['fragment']; - - return $location; - } - - - /** - * Sendet eine Redirect-Anweisung mit der aktuellen URL an den Browser. - */ - public function sendRedirect() - { - $location = $this->getUrl(); - - header('Location: '.$location); - exit; - } - - - /** - * Führt einen HTTP-Request durch. - * - * @return boolean Erfolg der Anfrage. - */ - public function request() - { - $this->error = ''; - $this->status = ''; - - $errno = 0; - $errstr = ''; - - if ( empty($this->url['host']) ) - { - $this->error = "No hostname specified"; - return false; - } - - foreach( $this->header as $header_key=>$header_value ) - { - if ( is_numeric( $header_key ) ) - { - $dp = strpos($header_value,':'); - if ( $dp!==FALSE) - $this->header[substr($header_value,0,$dp)] = substr($header_value,$dp+1); - unset($this->header[$header_key]); - } - } - - $parameterString = $this->getParameterString(); - - if ( $this->method == 'POST' ) - { - $this->header['Content-Type' ] = 'application/x-www-form-urlencoded'; - $this->header['Content-Length'] = strlen($parameterString); - } - - // Accept-Header setzen, falls noch nicht vorhanden. - if ( !array_key_exists('Accept',$this->header) ) - $this->header['Accept'] = '*/*'; - - $this->responseHeader = array(); - - // RFC 1945 (Section 9.3) says: - // A user agent should never automatically redirect a request - // more than 5 times, since such redirections usually indicate an infinite loop. - for( $r=1; $r<=5; $r++ ) - { - $this->header['Host'] = $this->url['host']; - - // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig). - if ( $this->url['scheme'] == 'https' || $this->url['port'] == '443' ) - $prx_proto = 'ssl://'; // SSL - else - $prx_proto = 'tcp://'; // Default - - $fp = @fsockopen ($prx_proto.$this->url['host'],$this->url['port'], $errno, $errstr, 30); - - if ( !$fp || !is_resource($fp) ) - { - // Keine Verbindung zum Host moeglich. - $this->error = "Connection refused: '".$prx_proto.$this->url['host'].':'.$this->url['port']." - $errstr ($errno)"; - return false; - } - else - { - - $lb = "\r\n"; - $http_get = $this->url['path']; - - $request_header = array( $this->method.' '.$http_get.' HTTP/1.0'); - - foreach($this->header as $header_key=>$header_value) - $request_header[] = $header_key.': '.$header_value; - - $http_request = implode($lb,$request_header).$lb.$lb; - - if ( $this->method == 'GET') - if ( !empty($parameterString) ) - $http_get .= '?'.$parameterString; - - if ( $this->method == 'POST' ) - $http_request .= $parameterString; - - if (!is_resource($fp)) { - $this->error = 'Connection lost after connect: '.$prx_proto.$this->url['host'].':'.$this->url['port']; - return false; - } - fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden. - - // Jetzt erfolgt das Auslesen der HTTP-Antwort. - $isHeader = true; - - // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor - // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP - if (!is_resource($fp)) { - $this->error = 'Connection lost during transfer: '.$this->url['host'].':'.$this->url['port']; - return false; - } - elseif (!feof($fp)) { - $line = fgets($fp,1028); - $this->status = substr($line,9,3); - } - else - { - $this->error = 'Unexpected EOF while reading HTTP-Response'; - return false; - } - - $this->body = ''; - while (!feof($fp)) { - $line = fgets($fp,1028); - if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header. - { - $isHeader = false; - } - elseif( $isHeader ) - { - list($headerName,$headerValue) = explode(': ',$line) + array(1=>''); - $this->responseHeader[$headerName] = trim($headerValue); - } - else - { - $this->body .= $line; - } - } - fclose($fp); // Verbindung brav schlie�en. - - - // RFC 1945 (Section 6.1.1) schreibt - // "[...] However, applications must understand the class of any status code, as - // indicated by the first digit" - // Daher interessiert uns nur die erste Stelle des 3-stelligen HTTP-Status. - - // 301 Moved Permanently - // 302 Moved Temporarily - if ( $this->status == '301' || - $this->status == '302' ) - { - $location = @$this->responseHeader['Location']; - if ( empty($location) ) - { - $this->error = '301/302 Response without Location-header'; - return false; - } - - //Html::debug($this->url,"alte URL"); - //Html::debug($location,"NEUES REDIRECT AUF"); - $this->setURL($location); - continue; // Naechster Versuch mit umgeleiteter Adresse. - } - - // RFC 1945 (Section 6.1.1) schreibt - // "2xx: Success - The action was successfully received, understood, and accepted." - elseif ( substr($this->status,0,1) == '2' ) - { - return true; - } - elseif ( substr($this->status,0,1) == '4' ) - { - $this->error = 'Client Error: '.$this->status; - return false; - } - elseif ( substr($this->status,0,1) == '5' ) - { - $this->error = 'Server Error: '.$this->status; - return false; - } - else - { - $this->error = 'Unexpected HTTP-Status: '.$this->status. '; this is mostly a client error, sorry.'; - return false; - } - } - - $this->error = 'Too much redirects, infinite loop assumed. Exiting. Last URL: '.$http_get; - return false; - - } - - } - - - /** - * Aus dem HTTP-Header werden die vom Browser angeforderten Sprachen - * gelesen.<br> - * Es wird eine Liste von Sprachen erzeugt.<br> - * - * Beispiel: - * 'de_DE','de','en_GB','en' ... usw.<br> - * Wenn der Browser 'de_DE' anfordert, wird hier zusätzlich - * auch 'de' (als Fallback) ermittelt. - * - * @static - * @return Array - */ - public static function getLanguages() - { - global $HTTP_SERVER_VARS; - - $languages = array(); - $http_languages = @$HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE']; - foreach( explode(',',$http_languages) as $l ) - { - list($part) = explode(';',$l); // Priorit�ten ignorieren. - $languages[] = trim($part); - - // Aus "de_DE" das "de" extrahieren. - $languages[] = current(explode('_',str_replace('-','_',trim($part)))); - } - - return array_unique( $languages ); - } - - - /** - * Ermittelt die aktuelle HTTP-Adresse des Requests (inkl. Pfad, jedoch ohne Datei). - * - * @return String URL - */ - public static function getServer() - { - $https = getenv('HTTPS'); - - if ( $https ) - $server = 'https://'; - else - $server = 'http://'; - - $server .= getenv('SERVER_NAME').dirname(getenv('REQUEST_URI')); - - return $server; - } - - - - /** - * Server-Fehlermeldung anzeigen.<br> - * - * Erzeugt einen "HTTP 501 Internal Server Error". Zusaetzlich - * wird ein 'rollback' auf der Datenbank ausgefaehrt. - * - * @param String $message Eigener Hinweistext - */ - public static function serverError($message,$reason='') - { - if ( class_exists('Session')) - { - $db = Session::getDatabase(); - if ( is_object( $db ) ) - $db->rollback(); - } - - if ( class_exists('Logger')) - Logger::warn($message."\n".$reason); - - Http::sendStatus(501,'Internal Server Error',$message,$reason); - } - - - - /** - * Der Benutzer ist nicht autorisiert, eine Aktion auszufuehren. - * - * Diese Funktion erzeugt einen "HTTP 403 Not Authorized" und das - * Skript wird beendet. - * - * @param String $text Text - * @param String $message Eigener Hinweistext - */ - public static function notAuthorized($message='') - { - Logger::warn("Security warning: $message"); - Http::sendStatus(403,'Not authorized',$message); - } - - - - - - - /** - * Nichts gefunden. - * - * Diese Funktion erzeugt einen "HTTP 404 Not found" und das - * Skript wird beendet. - * - * @param String $text Text - * @param String $message Eigener Hinweistext - */ - public static function notFound($text,$message) - { - Http::sendStatus(404,'Not found',$message); - } - - - - /** - * Kein Inhalt. - * - * Die HTTP-Antwort stellt gegenüber dem Client klar, dass es keinen Inhalt gibt. - */ - public static function noContent() - { - header('HTTP/1.0 204 No Content'); - exit; - } - - - - /** - * Schickt einen HTTP-Status zum Client und beendet das Skript. - * - * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501) - * @param String $text HTTP-Meldung (Default: 'Internal Server Error') - * @param String $message Eigener Hinweistext (Default: leer) - * @param String $reason Technischer Grund (Default: leer) - */ - public static function sendStatus( $status=501,$text='Internal Server Error',$message='',$reason='' ) - { - if ( headers_sent() ) - { - echo "$status $text\n$message"; - exit; - } - - header('HTTP/1.0 '.intval($status).' '.$text); - - - $types = Http::getAccept(); - - if ( @$_REQUEST['output']=='json' || sizeof($types)==1 && in_array('application/json',$types) ) - { - header('Content-Type: application/json'); - require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT ); - $json = new JSON(); - echo $json->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) ); - } - elseif ( @$_REQUEST['output']=='xml' || sizeof($types)==1 && in_array('application/xml',$types) ) - { - header('Content-Type: application/xml'); - require_once( OR_SERVICECLASSES_DIR."XML.class.".PHP_EXT ); - $xml = new XML(); - $xml->root='error'; - echo $xml->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) ); - } - else - { - header('Content-Type: text/html'); - $message = htmlentities($message); - $reason = htmlentities($reason ); - $signature = OR_TITLE.' '.OR_VERSION.' '.getenv('SERVER_SOFTWARE'); - echo <<<HTML -<html> -<head><title>$status $text - OpenRat</title></head> -<body> -<h1>$text</h1> -<p>$message</p> -<pre>$reason</pre> -<hr> -<address>$signature</adddress> -</body> -</html> -HTML; - } - exit; - } - - - /** - * Liefert den Mime-Type, den der Browser (oder besser: HTTP-Client) wünscht. - * - * @return Array Mime-Typen, welche vom User-Agent akzeptiert werden. - */ - public static function getAccept() - { - $httpAccept = getenv('HTTP_ACCEPT'); - return $types = explode(',',$httpAccept); - } - - - - /** - * Liefert die IPv4-Adresse des Clients. Falls der Request durch einen Proxy kam, wird - * versucht, die echte IP-Adresse aus dem Anfrageheader zu ermitteln. - * - * @return Client-IPv4-Adresse - */ - public static function getClientIP() - { - $ip = ''; - - if ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) ) - { - $ip = $_SERVER["HTTP_X_FORWARDED_FOR"]; - } - elseif ( isset($_SERVER["HTTP_CLIENT_IP"]) ) - { - $ip = $_SERVER["HTTP_CLIENT_IP"]; - } - elseif ( isset($_SERVER["REMOTE_ADDR"]) ) - { - $ip = $_SERVER["REMOTE_ADDR"]; - } - - return $ip; - } - - - - /** - * Ermittelt den TCP/IP-Port des Clients. - * Achtung, bei Proxy-Zugriffen kann dies der Port des Proxys sein. - * - * @return TCP/IP-Port - */ - public static function getClientPort() - { - $ip = ''; - - if ( isset($_SERVER["REMOTE_PORT"]) ) - { - $ip = $_SERVER["REMOTE_PORT"]; - } - - return $ip; - } -} - -?>- \ No newline at end of file diff --git a/util/JSON.class.php b/util/JSON.class.php @@ -1,809 +0,0 @@ -<?php -/** - * Converts to and from JSON format. - * - * JSON (JavaScript Object Notation) is a lightweight data-interchange - * format. It is easy for humans to read and write. It is easy for machines - * to parse and generate. It is based on a subset of the JavaScript - * Programming Language, Standard ECMA-262 3rd Edition - December 1999. - * This feature can also be found in Python. JSON is a text format that is - * completely language independent but uses conventions that are familiar - * to programmers of the C-family of languages, including C, C++, C#, Java, - * JavaScript, Perl, TCL, and many others. These properties make JSON an - * ideal data-interchange language. - * - * This package provides a simple encoder and decoder for JSON notation. It - * is intended for use with client-side Javascript applications that make - * use of HTTPRequest to perform server communication functions - data can - * be encoded into JSON notation for use in a client-side javascript, or - * decoded from incoming Javascript requests. JSON format is native to - * Javascript, and can be directly eval()'ed with no further parsing - * overhead - * - * All strings should be in ASCII or UTF-8 format! - * - * LICENSE: Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: Redistributions of source code must retain the - * above copyright notice, this list of conditions and the following - * disclaimer. Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * @category - * @package Services_JSON - * @author Michal Migurski <mike-json@teczno.com> - * @author Matt Knapp <mdknapp[at]gmail[dot]com> - * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> - * @author Jan Dankert - * @copyright 2005 Michal Migurski - * @version CVS: $Id$ - * @license http://www.opensource.org/licenses/bsd-license.php - * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 - */ - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_INDENT', "\t"); - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_SLICE', 1); - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_IN_STR', 2); - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_IN_ARR', 3); - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_IN_OBJ', 4); - -/** - * Marker constant for Services_JSON::decode(), used to flag stack state - */ -define('SERVICES_JSON_IN_CMT', 5); - -/** - * Behavior switch for Services_JSON::decode() - */ -define('SERVICES_JSON_LOOSE_TYPE', 16); - -/** - * Behavior switch for Services_JSON::decode() - */ -define('SERVICES_JSON_SUPPRESS_ERRORS', 32); - -/** - * Converts to and from JSON format. - * - * Brief example of use: - * - * <code> - * // create a new instance of Services_JSON - * $json = new Services_JSON(); - * - * // convert a complexe value to JSON notation, and send it to the browser - * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); - * $output = $json->encode($value); - * - * print($output); - * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] - * - * // accept incoming POST data, assumed to be in JSON notation - * $input = file_get_contents('php://input', 1000000); - * $value = $json->decode($input); - * </code> - */ -class JSON -{ - /** - * constructs a new JSON instance - * - * @param int $use object behavior flags; combine with boolean-OR - * - * possible values: - * - SERVICES_JSON_LOOSE_TYPE: loose typing. - * "{...}" syntax creates associative arrays - * instead of objects in decode(). - * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. - * Values which can't be encoded (e.g. resources) - * appear as NULL instead of throwing errors. - * By default, a deeply-nested resource will - * bubble up with an error, so all return values - * from encode() should be checked with isError() - */ - function Services_JSON() - { - $this->use = SERVICES_JSON_LOOSE_TYPE; - } - - /** - * convert a string from one UTF-16 char to one UTF-8 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf16 UTF-16 character - * @return string UTF-8 character - * @access private - */ - function utf162utf8($utf16) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); - } - - $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); - - switch(true) { - case ((0x7F & $bytes) == $bytes): - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x7F & $bytes); - - case (0x07FF & $bytes) == $bytes: - // return a 2-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xC0 | (($bytes >> 6) & 0x1F)) - . chr(0x80 | ($bytes & 0x3F)); - - case (0xFFFF & $bytes) == $bytes: - // return a 3-byte UTF-8 character - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0xE0 | (($bytes >> 12) & 0x0F)) - . chr(0x80 | (($bytes >> 6) & 0x3F)) - . chr(0x80 | ($bytes & 0x3F)); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * convert a string from one UTF-8 char to one UTF-16 char - * - * Normally should be handled by mb_convert_encoding, but - * provides a slower PHP-only method for installations - * that lack the multibye string extension. - * - * @param string $utf8 UTF-8 character - * @return string UTF-16 character - * @access private - */ - function utf82utf16($utf8) - { - // oh please oh please oh please oh please oh please - if(function_exists('mb_convert_encoding')) { - return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); - } - - switch(strlen($utf8)) { - case 1: - // this case should never be reached, because we are in ASCII range - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return $utf8; - - case 2: - // return a UTF-16 character from a 2-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr(0x07 & (ord($utf8{0}) >> 2)) - . chr((0xC0 & (ord($utf8{0}) << 6)) - | (0x3F & ord($utf8{1}))); - - case 3: - // return a UTF-16 character from a 3-byte UTF-8 char - // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - return chr((0xF0 & (ord($utf8{0}) << 4)) - | (0x0F & (ord($utf8{1}) >> 2))) - . chr((0xC0 & (ord($utf8{1}) << 6)) - | (0x7F & ord($utf8{2}))); - } - - // ignoring UTF-32 for now, sorry - return ''; - } - - /** - * encodes an arbitrary variable into JSON format - * - * @param mixed $var any number, boolean, string, array, or object to be encoded. - * see argument 1 to Services_JSON() above for array-parsing behavior. - * if var is a strng, note that encode() always expects it - * to be in ASCII or UTF-8 format! - * - * @return mixed JSON string representation of input var or an error if a problem occurs - * @access public - */ - function encode($var) - { - static $indentNr = 0; - - $indent = str_repeat(SERVICES_JSON_INDENT,$indentNr); - - switch (gettype($var)) { - case 'boolean': - return $var ? 'true' : 'false'; - - case 'NULL': - return 'null'; - - case 'integer': - return (int) $var; - - case 'double': - case 'float': - return (float) $var; - - case 'string': - // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT - $ascii = ''; - $strlen_var = strlen($var); - - /* - * Iterate over every character in the string, - * escaping with a slash or encoding to UTF-8 where necessary - */ - for ($c = 0; $c < $strlen_var; ++$c) { - - $ord_var_c = ord($var{$c}); - - switch (true) { - case $ord_var_c == 0x08: - $ascii .= '\b'; - break; - case $ord_var_c == 0x09: - $ascii .= '\t'; - break; - case $ord_var_c == 0x0A: - $ascii .= '\n'; - break; - case $ord_var_c == 0x0C: - $ascii .= '\f'; - break; - case $ord_var_c == 0x0D: - $ascii .= '\r'; - break; - - case $ord_var_c == 0x22: - case $ord_var_c == 0x2F: - case $ord_var_c == 0x5C: - // double quote, slash, slosh - $ascii .= '\\'.$var{$c}; - break; - - case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): - // characters U-00000000 - U-0000007F (same as ASCII) - $ascii .= $var{$c}; - break; - - case (($ord_var_c & 0xE0) == 0xC0): - // characters U-00000080 - U-000007FF, mask 110XXXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, ord($var{$c + 1})); - $c += 1; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF0) == 0xE0): - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - @ord($var{$c + 1}), - @ord($var{$c + 2})); - $c += 2; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xF8) == 0xF0): - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - @ord($var{$c + 1}), - @ord($var{$c + 2}), - @ord($var{$c + 3})); - $c += 3; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xFC) == 0xF8): - // characters U-00200000 - U-03FFFFFF, mask 111110XX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - @ord($var{$c + 1}), - @ord($var{$c + 2}), - @ord($var{$c + 3}), - @ord($var{$c + 4})); - $c += 4; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - - case (($ord_var_c & 0xFE) == 0xFC): - // characters U-04000000 - U-7FFFFFFF, mask 1111110X - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $char = pack('C*', $ord_var_c, - @ord($var{$c + 1}), - @ord($var{$c + 2}), - @ord($var{$c + 3}), - @ord($var{$c + 4}), - @ord(@$var{$c + 5})); - $c += 5; - $utf16 = $this->utf82utf16($char); - $ascii .= sprintf('\u%04s', bin2hex($utf16)); - break; - } - } - - return '"'.$ascii.'"'; - - case 'array': - /* - * As per JSON spec if any array key is not an integer - * we must treat the the whole array as an object. We - * also try to catch a sparsely populated associative - * array with numeric keys here because some JS engines - * will create an array with empty indexes up to - * max_index which can cause memory issues and because - * the keys, which may be relevant, will be remapped - * otherwise. - * - * As per the ECMA and JSON specification an object may - * have any string as a property. Unfortunately due to - * a hole in the ECMA specification if the key is a - * ECMA reserved word or starts with a digit the - * parameter is only accessible using ECMAScript's - * bracket notation. - */ - - // treat as a JSON object - if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { - $indentNr++; - $properties = array_map(array($this, 'name_value'), - array_keys($var), - array_values($var)); - $indentNr--; - - foreach($properties as $property) { - if(JSON::isError($property)) { - return $property; - } - } - - return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) ."\n$indent".'}'."\n$indent"; - } - - // treat it like a regular array - $indentNr++; - $elements = array_map(array($this, 'encode'), $var); - $indentNr--; - - foreach($elements as $element) { - if(JSON::isError($element)) { - return $element; - } - } - - return "\n$indent".'['."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $elements) . "\n$indent".']'."\n$indent"; - - case 'object': - $vars = get_object_vars($var); - - $indentNr++; - $properties = array_map(array($this, 'name_value'), - array_keys($vars), - array_values($vars)); - $indentNr--; - - foreach($properties as $property) { - if(JSON::isError($property)) { - return $property; - } - } - - return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) . "\n$indent".'}'."\n$indent"; - - default: - return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) - ? 'null' - : new JSON_Error(gettype($var)." can not be encoded as JSON string"); - } - } - - - - /** - * array-walking function for use in generating JSON-formatted name-value pairs - * - * @param string $name name of key to use - * @param mixed $value reference to an array element to be encoded - * - * @return string JSON-formatted name-value pair, like '"name":value' - * @access private - */ - function name_value($name, $value ) - { - $encoded_value = $this->encode($value); - - if(JSON::isError($encoded_value)) { - return $encoded_value; - } - - return $this->encode(strval($name)) . ':' . $encoded_value; - } - - /** - * reduce a string by removing leading and trailing comments and whitespace - * - * @param $str string string value to strip of comments and whitespace - * - * @return string string value stripped of comments and whitespace - * @access private - */ - function reduce_string($str) - { - $str = preg_replace(array( - - // eliminate single line comments in '// ...' form - '#^\s*//(.+)$#m', - - // eliminate multi-line comments in '/* ... */' form, at start of string - '#^\s*/\*(.+)\*/#Us', - - // eliminate multi-line comments in '/* ... */' form, at end of string - '#/\*(.+)\*/\s*$#Us' - - ), '', $str); - - // eliminate extraneous space - return trim($str); - } - - /** - * decodes a JSON string into appropriate variable - * - * @param string $str JSON-formatted string - * - * @return mixed number, boolean, string, array, or object - * corresponding to given JSON input string. - * See argument 1 to Services_JSON() above for object-output behavior. - * Note that decode() always returns strings - * in ASCII or UTF-8 format! - * @access public - */ - function decode($str) - { - $str = $this->reduce_string($str); - - switch (strtolower($str)) { - case 'true': - return true; - - case 'false': - return false; - - case 'null': - return null; - - default: - $m = array(); - - if (is_numeric($str)) { - // Lookie-loo, it's a number - - // This would work on its own, but I'm trying to be - // good about returning integers where appropriate: - // return (float)$str; - - // Return float or int, as appropriate - return ((float)$str == (integer)$str) - ? (integer)$str - : (float)$str; - - } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { - // STRINGS RETURNED IN UTF-8 FORMAT - $delim = substr($str, 0, 1); - $chrs = substr($str, 1, -1); - $utf8 = ''; - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c < $strlen_chrs; ++$c) { - - $substr_chrs_c_2 = substr($chrs, $c, 2); - $ord_chrs_c = ord($chrs{$c}); - - switch (true) { - case $substr_chrs_c_2 == '\b': - $utf8 .= chr(0x08); - ++$c; - break; - case $substr_chrs_c_2 == '\t': - $utf8 .= chr(0x09); - ++$c; - break; - case $substr_chrs_c_2 == '\n': - $utf8 .= chr(0x0A); - ++$c; - break; - case $substr_chrs_c_2 == '\f': - $utf8 .= chr(0x0C); - ++$c; - break; - case $substr_chrs_c_2 == '\r': - $utf8 .= chr(0x0D); - ++$c; - break; - - case $substr_chrs_c_2 == '\\"': - case $substr_chrs_c_2 == '\\\'': - case $substr_chrs_c_2 == '\\\\': - case $substr_chrs_c_2 == '\\/': - if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || - ($delim == "'" && $substr_chrs_c_2 != '\\"')) { - $utf8 .= $chrs{++$c}; - } - break; - - case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): - // single, escaped unicode character - $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) - . chr(hexdec(substr($chrs, ($c + 4), 2))); - $utf8 .= $this->utf162utf8($utf16); - $c += 5; - break; - - case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): - $utf8 .= $chrs{$c}; - break; - - case ($ord_chrs_c & 0xE0) == 0xC0: - // characters U-00000080 - U-000007FF, mask 110XXXXX - //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 2); - ++$c; - break; - - case ($ord_chrs_c & 0xF0) == 0xE0: - // characters U-00000800 - U-0000FFFF, mask 1110XXXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 3); - $c += 2; - break; - - case ($ord_chrs_c & 0xF8) == 0xF0: - // characters U-00010000 - U-001FFFFF, mask 11110XXX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 4); - $c += 3; - break; - - case ($ord_chrs_c & 0xFC) == 0xF8: - // characters U-00200000 - U-03FFFFFF, mask 111110XX - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 5); - $c += 4; - break; - - case ($ord_chrs_c & 0xFE) == 0xFC: - // characters U-04000000 - U-7FFFFFFF, mask 1111110X - // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 - $utf8 .= substr($chrs, $c, 6); - $c += 5; - break; - - } - - } - - return $utf8; - - } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { - // array, or object notation - - if ($str{0} == '[') { - $stk = array(SERVICES_JSON_IN_ARR); - $arr = array(); - } else { - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = array(); - } else { - $stk = array(SERVICES_JSON_IN_OBJ); - $obj = new stdClass(); - } - } - - array_push($stk, array('what' => SERVICES_JSON_SLICE, - 'where' => 0, - 'delim' => false)); - - $chrs = substr($str, 1, -1); - $chrs = $this->reduce_string($chrs); - - if ($chrs == '') { - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } else { - return $obj; - - } - } - - //print("\nparsing {$chrs}\n"); - - $strlen_chrs = strlen($chrs); - - for ($c = 0; $c <= $strlen_chrs; ++$c) { - - $top = end($stk); - $substr_chrs_c_2 = substr($chrs, $c, 2); - - if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { - // found a comma that is not inside a string, array, etc., - // OR we've reached the end of the character list - $slice = substr($chrs, $top['where'], ($c - $top['where'])); - array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); - //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - // we are in an array, so just push an element onto the stack - array_push($arr, $this->decode($slice)); - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - // we are in an object, so figure - // out the property name and set an - // element in an associative array, - // for now - $parts = array(); - - if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // "name":value pair - $key = $this->decode($parts[1]); - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { - // name:value pair, where name is unquoted - $key = $parts[1]; - $val = $this->decode($parts[2]); - - if ($this->use & SERVICES_JSON_LOOSE_TYPE) { - $obj[$key] = $val; - } else { - $obj->$key = $val; - } - } - - } - - } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { - // found a quote, and we are not inside a string - array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); - //print("Found start of string at {$c}\n"); - - } elseif (($chrs{$c} == $top['delim']) && - ($top['what'] == SERVICES_JSON_IN_STR) && - ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { - // found a quote, we're in a string, and it's not escaped - // we know that it's not escaped becase there is _not_ an - // odd number of backslashes at the end of the string so far - array_pop($stk); - //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '[') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-bracket, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); - //print("Found start of array at {$c}\n"); - - } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { - // found a right-bracket, and we're in an array - array_pop($stk); - //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($chrs{$c} == '{') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a left-brace, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); - //print("Found start of object at {$c}\n"); - - } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { - // found a right-brace, and we're in an object - array_pop($stk); - //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } elseif (($substr_chrs_c_2 == '/*') && - in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { - // found a comment start, and we are in an array, object, or slice - array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); - $c++; - //print("Found start of comment at {$c}\n"); - - } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { - // found a comment end, and we're in one now - array_pop($stk); - $c++; - - for ($i = $top['where']; $i <= $c; ++$i) - $chrs = substr_replace($chrs, ' ', $i, 1); - - //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); - - } - - } - - if (reset($stk) == SERVICES_JSON_IN_ARR) { - return $arr; - - } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { - return $obj; - - } - - } - } - } - - /** - * @todo Ultimately, this should just call PEAR::isError() - */ - function isError($data, $code = null) - { - if (class_exists('pear')) { - return PEAR::isError($data, $code); - } elseif (is_object($data) && (get_class($data) == 'json_error' || - is_subclass_of($data, 'json_error'))) { - return true; - } - - return false; - } -} - - - /** - * @todo Ultimately, this class shall be descended from PEAR_Error - */ - class JSON_Error - { - function JSON_Error($message = 'unknown error', $code = null, - $mode = null, $options = null, $userinfo = null) - { - - } - } - - -?> diff --git a/util/JSqueeze.class.php b/util/JSqueeze.class.php @@ -1,1064 +0,0 @@ -<?php - -/* - * Copyright (C) 2016 Nicolas Grekas - p@tchwork.com - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the (at your option): - * Apache License v2.0 (see provided LICENCE.ASL20 file), or - * GNU General Public License v2.0 (see provided LICENCE.GPLv2 file). - */ - -//namespace Patchwork; - -/* -* -* This class shrinks Javascript code -* (a process called minification nowadays) -* -* Should work with most valid Javascript code, -* even when semi-colons are missing. -* -* Features: -* - Removes comments and white spaces. -* - Renames every local vars, typically to a single character. -* - Renames also global vars, methods and properties, but only if they -* are marked special by some naming convention. By default, special -* var names begin with one or more "$", or with a single "_". -* - Renames also local/global vars found in strings, -* but only if they are marked special. -* - Keep Microsoft's conditional comments. -* - Output is optimized for later HTTP compression. -* -* Notes: -* - Source code must be parse error free before processing. -* - In order to maximise later HTTP compression (deflate, gzip), -* new variables names are chosen by considering closures, -* variables' frequency and characters' frequency. -* - If you use with/eval then be careful. -* -* Bonus: -* - Replaces false/true by !1/!0 -* - Replaces new Array/Object by []/{} -* - Merges consecutive "var" declarations with commas -* - Merges consecutive concatened strings -* - Fix a bug in Safari's parser (http://forums.asp.net/thread/1585609.aspx) -* - Can replace optional semi-colons by line feeds, -* thus facilitating output debugging. -* - Keep important comments marked with /*!... -* - Treats three semi-colons ;;; like single-line comments -* (http://dean.edwards.name/packer/2/usage/#triple-semi-colon). -* - Fix special catch scope across browsers -* - Work around buggy-handling of named function expressions in IE<=8 -* -* TODO? -* - foo['bar'] => foo.bar -* - {'foo':'bar'} => {foo:'bar'} -* - Dead code removal (never used function) -* - Munge primitives: var WINDOW=window, etc. -*/ - -class JSqueeze -{ - const - - SPECIAL_VAR_PACKER = '(\$+[a-zA-Z_]|_[a-zA-Z0-9$])[a-zA-Z0-9_$]*'; - - public - - $charFreq; - - protected - - $strings, - $closures, - $str0, - $str1, - $argFreq, - $specialVarRx, - $keepImportantComments, - - $varRx = '(?:[a-zA-Z_$])[a-zA-Z0-9_$]*', - $reserved = array( - // Literals - 'true','false','null', - // ES6 - 'break','case','class','catch','const','continue','debugger','default','delete','do','else','export','extends','finally','for','function','if','import','in','instanceof','new','return','super','switch','this','throw','try','typeof','var','void','while','with','yield', - // Future - 'enum', - // Strict mode - 'implements','package','protected','static','let','interface','private','public', - // Module - 'await', - // Older standards - 'abstract','boolean','byte','char','double','final','float','goto','int','long','native','short','synchronized','throws','transient','volatile', - ); - - public function __construct() - { - $this->reserved = array_flip($this->reserved); - $this->charFreq = array_fill(0, 256, 0); - } - - /** - * Squeezes a JavaScript source code. - * - * Set $singleLine to false if you want optional - * semi-colons to be replaced by line feeds. - * - * Set $keepImportantComments to false if you want /*! comments to be removed. - * - * $specialVarRx defines the regular expression of special variables names - * for global vars, methods, properties and in string substitution. - * Set it to false if you don't want any. - * - * If the analysed javascript source contains a single line comment like - * this one, then the directive will overwrite $specialVarRx: - * - * // jsqueeze.specialVarRx = your_special_var_regexp_here - * - * Only the first directive is parsed, others are ignored. It is not possible - * to redefine $specialVarRx in the middle of the javascript source. - * - * Example: - * $parser = new JSqueeze; - * $squeezed_js = $parser->squeeze($fat_js); - */ - public function squeeze($code, $singleLine = true, $keepImportantComments = true, $specialVarRx = false) - { - $code = trim($code); - if ('' === $code) { - return ''; - } - - $this->argFreq = array(-1 => 0); - $this->specialVarRx = $specialVarRx; - $this->keepImportantComments = !!$keepImportantComments; - - if (preg_match("#//[ \t]*jsqueeze\.specialVarRx[ \t]*=[ \t]*([\"']?)(.*)\\1#i", $code, $key)) { - if (!$key[1]) { - $key[2] = trim($key[2]); - $key[1] = strtolower($key[2]); - $key[1] = $key[1] && $key[1] != 'false' && $key[1] != 'none' && $key[1] != 'off'; - } - - $this->specialVarRx = $key[1] ? $key[2] : false; - } - - // Remove capturing parentheses - $this->specialVarRx && $this->specialVarRx = preg_replace('/(?<!\\\\)((?:\\\\\\\\)*)\((?!\?)/', '(?:', $this->specialVarRx); - - false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n"); - false !== strpos($code, "\xC2\x85") && $code = str_replace("\xC2\x85", "\n", $code); // Next Line - false !== strpos($code, "\xE2\x80\xA8") && $code = str_replace("\xE2\x80\xA8", "\n", $code); // Line Separator - false !== strpos($code, "\xE2\x80\xA9") && $code = str_replace("\xE2\x80\xA9", "\n", $code); // Paragraph Separator - - list($code, $this->strings) = $this->extractStrings($code); - list($code, $this->closures) = $this->extractClosures($code); - - $key = "//''\"\"#0'"; // This crap has a wonderful property: it can not happen in any valid javascript, even in strings - $this->closures[$key] = &$code; - - $tree = array($key => array('parent' => false)); - $this->makeVars($code, $tree[$key], $key); - $this->renameVars($tree[$key], true); - - $code = substr($tree[$key]['code'], 1); - $code = preg_replace("'\breturn !'", 'return!', $code); - $code = preg_replace("'\}(?=(else|while)[^\$.a-zA-Z0-9_])'", "}\r", $code); - $code = str_replace(array_keys($this->strings), array_values($this->strings), $code); - - if ($singleLine) { - $code = strtr($code, "\n", ';'); - } else { - $code = str_replace("\n", ";\n", $code); - } - false !== strpos($code, "\r") && $code = strtr(trim($code), "\r", "\n"); - - // Cleanup memory - $this->charFreq = array_fill(0, 256, 0); - $this->strings = $this->closures = $this->argFreq = array(); - $this->str0 = $this->str1 = ''; - - return $code; - } - - protected function extractStrings($f) - { - if ($cc_on = false !== strpos($f, '@cc_on')) { - // Protect conditional comments from being removed - $f = str_replace('#', '##', $f); - $f = str_replace('/*@', '1#@', $f); - $f = preg_replace("'//@([^\n]+)'", '2#@$1@#3', $f); - $f = str_replace('@*/', '@#1', $f); - } - - $len = strlen($f); - $code = str_repeat(' ', $len); - $j = 0; - - $strings = array(); - $K = 0; - - $instr = false; - - $q = array( - "'", '"', - "'" => 0, - '"' => 0, - ); - - // Extract strings, removes comments - for ($i = 0; $i < $len; ++$i) { - if ($instr) { - if ('//' == $instr) { - if ("\n" == $f[$i]) { - $f[$i--] = ' '; - $instr = false; - } - } elseif ($f[$i] == $instr || ('/' == $f[$i] && "/'" == $instr)) { - if ('!' == $instr) { - } elseif ('*' == $instr) { - if ('/' == $f[$i + 1]) { - ++$i; - $instr = false; - } - } else { - if ("/'" == $instr) { - while (isset($f[$i + 1]) && false !== strpos('gmi', $f[$i + 1])) { - $s[] = $f[$i++]; - } - $s[] = $f[$i]; - } - - $instr = false; - } - } elseif ('*' == $instr) { - } elseif ('!' == $instr) { - if ('*' == $f[$i] && '/' == $f[$i + 1]) { - $s[] = "*/\r"; - ++$i; - $instr = false; - } elseif ("\n" == $f[$i]) { - $s[] = "\r"; - } else { - $s[] = $f[$i]; - } - } elseif ('\\' == $f[$i]) { - ++$i; - - if ("\n" != $f[$i]) { - isset($q[$f[$i]]) && ++$q[$f[$i]]; - $s[] = '\\'.$f[$i]; - } - } elseif ('[' == $f[$i] && "/'" == $instr) { - $instr = '/['; - $s[] = '['; - } elseif (']' == $f[$i] && '/[' == $instr) { - $instr = "/'"; - $s[] = ']'; - } elseif ("'" == $f[$i] || '"' == $f[$i]) { - ++$q[$f[$i]]; - $s[] = '\\'.$f[$i]; - } else { - $s[] = $f[$i]; - } - } else { - switch ($f[$i]) { - case ';': - // Remove triple semi-colon - if ($i > 0 && ';' == $f[$i - 1] && $i + 1 < $len && ';' == $f[$i + 1]) { - $f[$i] = $f[$i + 1] = '/'; - } else { - $code[++$j] = ';'; - break; - } - - case '/': - if ('*' == $f[$i + 1]) { - ++$i; - $instr = '*'; - - if ($this->keepImportantComments && '!' == $f[$i + 1]) { - ++$i; - // no break here - } else { - break; - } - } elseif ('/' == $f[$i + 1]) { - ++$i; - $instr = '//'; - break; - } else { - $a = $j && (' ' == $code[$j] || "\x7F" == $code[$j]) ? $code[$j - 1] : $code[$j]; - if (false !== strpos('-!%&;<=>~:^+|,()*?[{} ', $a) - || (false !== strpos('oenfd', $a) - && preg_match( - "'(?<![\$.a-zA-Z0-9_])(do|else|return|typeof|yield[ \x7F]?\*?)[ \x7F]?$'", - substr($code, $j - 7, 8) - )) - ) { - if (')' === $a && $j > 1) { - $a = 1; - $k = $j - (' ' == $code[$j] || "\x7F" == $code[$j]) - 1; - while ($k >= 0 && $a) { - if ('(' === $code[$k]) { - --$a; - } elseif (')' === $code[$k]) { - ++$a; - } - --$k; - } - if (!preg_match("'(?<![\$.a-zA-Z0-9_])(if|for|while)[ \x7F]?$'", substr($code, 0, $k + 1))) { - $code[++$j] = '/'; - break; - } - } - - $key = "//''\"\"".$K++.$instr = "/'"; - $a = $j; - $code .= $key; - while (isset($key[++$j - $a - 1])) { - $code[$j] = $key[$j - $a - 1]; - } - --$j; - isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); - $strings[$key] = array('/'); - $s = &$strings[$key]; - } else { - $code[++$j] = '/'; - } - - break; - } - - case "'": - case '"': - $instr = $f[$i]; - $key = "//''\"\"".$K++.('!' == $instr ? ']' : "'"); - $a = $j; - $code .= $key; - while (isset($key[++$j - $a - 1])) { - $code[$j] = $key[$j - $a - 1]; - } - --$j; - isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); - $strings[$key] = array(); - $s = &$strings[$key]; - '!' == $instr && $s[] = "\r/*!"; - - break; - - case "\n": - if ($j > 3) { - if (' ' == $code[$j] || "\x7F" == $code[$j]) { - --$j; - } - - $code[++$j] = - false !== strpos('kend+-', $code[$j - 1]) - && preg_match( - "'(?:\+\+|--|(?<![\$.a-zA-Z0-9_])(break|continue|return|yield[ \x7F]?\*?))[ \x7F]?$'", - substr($code, $j - 8, 9) - ) - ? ';' : "\x7F"; - - break; - } - - case "\t": $f[$i] = ' '; - case ' ': - if (!$j || ' ' == $code[$j] || "\x7F" == $code[$j]) { - break; - } - - default: - $code[++$j] = $f[$i]; - } - } - } - - isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s); - unset($s); - - $code = substr($code, 0, $j + 1); - $cc_on && $this->restoreCc($code, false); - - // Protect wanted spaces and remove unwanted ones - $code = strtr($code, "\x7F", ' '); - $code = str_replace('- -', "-\x7F-", $code); - $code = str_replace('+ +', "+\x7F+", $code); - $code = preg_replace("'(\d)\s+\.\s*([a-zA-Z\$_[(])'", "$1\x7F.$2", $code); - $code = preg_replace("# ([-!%&;<=>~:.^+|,()*?[\]{}/']+)#", '$1', $code); - $code = preg_replace("#([-!%&;<=>~:.^+|,()*?[\]{}/]+) #", '$1', $code); - $cc_on && $code = preg_replace_callback("'//[^\'].*?@#3'", function ($m) { return strtr($m[0], ' ', "\x7F"); }, $code); - - // Replace new Array/Object by []/{} - false !== strpos($code, 'new Array') && $code = preg_replace("'new Array(?:\(\)|([;\])},:]))'", '[]$1', $code); - false !== strpos($code, 'new Object') && $code = preg_replace("'new Object(?:\(\)|([;\])},:]))'", '{}$1', $code); - - // Add missing semi-colons after curly braces - // This adds more semi-colons than strictly needed, - // but it seems that later gzipping is favorable to the repetition of "};" - $code = preg_replace("'\}(?![:,;.()\[\]}\|&]|(else|catch|finally|while)[^\$.a-zA-Z0-9_])'", '};', $code); - - // Tag possible empty instruction for easy detection - $code = preg_replace("'(?<![\$.a-zA-Z0-9_])if\('", '1#(', $code); - $code = preg_replace("'(?<![\$.a-zA-Z0-9_])for\('", '2#(', $code); - $code = preg_replace("'(?<![\$.a-zA-Z0-9_])while\('", '3#(', $code); - - $forPool = array(); - $instrPool = array(); - $s = 0; - - $f = array(); - $j = -1; - - // Remove as much semi-colon as possible - $len = strlen($code); - for ($i = 0; $i < $len; ++$i) { - switch ($code[$i]) { - case '(': - if ($j >= 0 && "\n" == $f[$j]) { - $f[$j] = ';'; - } - - ++$s; - - if ($i && '#' == $code[$i - 1]) { - $instrPool[$s - 1] = 1; - if ('2' == $code[$i - 2]) { - $forPool[$s] = 1; - } - } - - $f[++$j] = '('; - break; - - case ']': - case ')': - if ($i + 1 < $len && !isset($forPool[$s]) && !isset($instrPool[$s - 1]) && preg_match("'[a-zA-Z0-9_\$]'", $code[$i + 1])) { - $f[$j] .= $code[$i]; - $f[++$j] = "\n"; - } else { - $f[++$j] = $code[$i]; - } - - if (')' == $code[$i]) { - unset($forPool[$s]); - --$s; - } - - continue 2; - - case '}': - if ("\n" == $f[$j]) { - $f[$j] = '}'; - } else { - $f[++$j] = '}'; - } - break; - - case ';': - if (isset($forPool[$s]) || isset($instrPool[$s])) { - $f[++$j] = ';'; - } elseif ($j >= 0 && "\n" != $f[$j] && ';' != $f[$j]) { - $f[++$j] = "\n"; - } - - break; - - case '#': - switch ($f[$j]) { - case '1': $f[$j] = 'if'; break 2; - case '2': $f[$j] = 'for'; break 2; - case '3': $f[$j] = 'while'; break 2; - } - - case '['; - if ($j >= 0 && "\n" == $f[$j]) { - $f[$j] = ';'; - } - - default: $f[++$j] = $code[$i]; - } - - unset($instrPool[$s]); - } - - $f = implode('', $f); - $cc_on && $f = str_replace('@#3', "\r", $f); - - // Fix "else ;" empty instructions - $f = preg_replace("'(?<![\$.a-zA-Z0-9_])else\n'", "\n", $f); - - $r1 = array( // keywords with a direct object - 'case','delete','do','else','function','in','instanceof','of','break', - 'new','return','throw','typeof','var','void','yield','let','if', - 'const','get','set', - ); - - $r2 = array( // keywords with a subject - 'in','instanceof','of', - ); - - // Fix missing semi-colons - $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])".implode(')(?<!(?<![a-zA-Z0-9_\$])', $r1).') (?!('.implode('|', $r2).")(?![a-zA-Z0-9_\$]))'", "\n", $f); - $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])do)(?<!(?<![a-zA-Z0-9_\$])else) if\('", "\nif(", $f); - $f = preg_replace("'(?<=--|\+\+)(?<![a-zA-Z0-9_\$])(".implode('|', $r1).")(?![a-zA-Z0-9_\$])'", "\n$1", $f); - $f = preg_replace("'(?<![a-zA-Z0-9_\$])for\neach\('", 'for each(', $f); - $f = preg_replace("'(?<![a-zA-Z0-9_\$])\n(".implode('|', $r2).")(?![a-zA-Z0-9_\$])'", '$1', $f); - - // Merge strings - if ($q["'"] > $q['"']) { - $q = array($q[1], $q[0]); - } - $f = preg_replace("#//''\"\"[0-9]+'#", $q[0].'$0'.$q[0], $f); - strpos($f, $q[0].'+'.$q[0]) && $f = str_replace($q[0].'+'.$q[0], '', $f); - $len = count($strings); - foreach ($strings as $r1 => &$r2) { - $r2 = "/'" == substr($r1, -2) - ? str_replace(array("\\'", '\\"'), array("'", '"'), $r2) - : str_replace('\\'.$q[1], $q[1], $r2); - } - - // Restore wanted spaces - $f = strtr($f, "\x7F", ' '); - - return array($f, $strings); - } - - protected function extractClosures($code) - { - $code = ';'.$code; - - $this->argFreq[-1] += substr_count($code, '}catch('); - - if ($this->argFreq[-1]) { - // Special catch scope handling - - // FIXME: this implementation doesn't work with nested catch scopes who need - // access to their parent's caught variable (but who needs that?). - - $f = preg_split("@}catch\(({$this->varRx})@", $code, -1, PREG_SPLIT_DELIM_CAPTURE); - - $code = 'catch$scope$var'.mt_rand(); - $this->specialVarRx = $this->specialVarRx ? '(?:'.$this->specialVarRx.'|'.preg_quote($code).')' : preg_quote($code); - $i = count($f) - 1; - - while ($i) { - $c = 1; - $j = 0; - $l = strlen($f[$i]); - - while ($c && $j < $l) { - $s = $f[$i][$j++]; - $c += '(' == $s ? 1 : (')' == $s ? -1 : 0); - } - - if (!$c) { - do { - $s = $f[$i][$j++]; - $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0); - } while ($c && $j < $l); - } - - $c = preg_quote($f[$i - 1], '#'); - $f[$i - 2] .= '}catch('.preg_replace("#([.,{]?)(?<![a-zA-Z0-9_\$@]){$c}\\b#", '$1'.$code, $f[$i - 1].substr($f[$i], 0, $j)).substr($f[$i], $j); - - unset($f[$i--], $f[$i--]); - } - - $code = $f[0]; - } - - $f = preg_split("'(?<![a-zA-Z0-9_\$])((?:function[ (]|get |set ).*?\{)'", $code, -1, PREG_SPLIT_DELIM_CAPTURE); - $i = count($f) - 1; - $closures = array(); - - while ($i) { - $c = 1; - $j = 0; - $l = strlen($f[$i]); - - while ($c && $j < $l) { - $s = $f[$i][$j++]; - $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0); - } - - switch (substr($f[$i - 2], -1)) { - default: - if (false !== $c = strpos($f[$i - 1], ' ', 8)) { - break; - } - case false: case "\n": case ';': case '{': case '}': case ')': case ']': - $c = strpos($f[$i - 1], '(', 4); - } - - $l = "//''\"\"#$i'"; - $code = substr($f[$i - 1], $c); - $closures[$l] = $code.substr($f[$i], 0, $j); - $f[$i - 2] .= substr($f[$i - 1], 0, $c).$l.substr($f[$i], $j); - - if ('(){' !== $code) { - $j = substr_count($code, ','); - do { - isset($this->argFreq[$j]) ? ++$this->argFreq[$j] : $this->argFreq[$j] = 1; - } while ($j--); - } - - $i -= 2; - } - - return array($f[0], $closures); - } - - protected function makeVars($closure, &$tree, $key) - { - $tree['code'] = &$closure; - $tree['nfe'] = false; - $tree['used'] = array(); - $tree['local'] = array(); - - // Replace multiple "var" declarations by a single one - $closure = preg_replace_callback("'(?<=[\n\{\}])var [^\n\{\};]+(?:\nvar [^\n\{\};]+)+'", array($this, 'mergeVarDeclarations'), $closure); - - // Get all local vars (functions, arguments and "var" prefixed) - - $vars = &$tree['local']; - - if (preg_match("'^( [^(]*)?\((.*?)\)\{'", $closure, $v)) { - if ($v[1]) { - $vars[$tree['nfe'] = substr($v[1], 1)] = -1; - $tree['parent']['local'][';'.$key] = &$vars[$tree['nfe']]; - } - - if ($v[2]) { - $i = 0; - $v = explode(',', $v[2]); - foreach ($v as $w) { - $vars[$w] = $this->argFreq[$i++] - 1; // Give a bonus to argument variables - } - } - } - - $v = preg_split("'(?<![\$.a-zA-Z0-9_])var '", $closure); - if ($i = count($v) - 1) { - $w = array(); - - while ($i) { - $j = $c = 0; - $l = strlen($v[$i]); - - while ($j < $l) { - switch ($v[$i][$j]) { - case '(': case '[': case '{': - ++$c; - break; - - case ')': case ']': case '}': - if ($c-- <= 0) { - break 2; - } - break; - - case ';': case "\n": - if (!$c) { - break 2; - } - - default: - $c || $w[] = $v[$i][$j]; - } - - ++$j; - } - - $w[] = ','; - --$i; - } - - $v = explode(',', implode('', $w)); - foreach ($v as $w) { - if (preg_match("'^{$this->varRx}'", $w, $v)) { - isset($vars[$v[0]]) || $vars[$v[0]] = 0; - } - } - } - - if (preg_match_all("@function ({$this->varRx})//''\"\"#@", $closure, $v)) { - foreach ($v[1] as $w) { - isset($vars[$w]) || $vars[$w] = 0; - } - } - - if ($this->argFreq[-1] && preg_match_all("@}catch\(({$this->varRx})@", $closure, $v)) { - $v[0] = array(); - foreach ($v[1] as $w) { - isset($v[0][$w]) ? ++$v[0][$w] : $v[0][$w] = 1; - } - foreach ($v[0] as $w => $v) { - $vars[$w] = $this->argFreq[-1] - $v; - } - } - - // Get all used vars, local and non-local - - $vars = &$tree['used']; - - if (preg_match_all("#([.,{]?(?:[gs]et )?)(?<![a-zA-Z0-9_\$])({$this->varRx})(:?)#", $closure, $w, PREG_SET_ORDER)) { - foreach ($w as $k) { - if (isset($k[1][0]) && (',' === $k[1][0] || '{' === $k[1][0])) { - if (':' === $k[3]) { - $k = '.'.$k[2]; - } elseif ('get ' === substr($k[1], 1, 4) || 'set ' === substr($k[1], 1, 4)) { - ++$this->charFreq[ord($k[1][1])]; // "g" or "s" - ++$this->charFreq[101]; // "e" - ++$this->charFreq[116]; // "t" - $k = '.'.$k[2]; - } else { - $k = $k[2]; - } - } else { - $k = $k[1].$k[2]; - } - - isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1; - } - } - - if (preg_match_all("#//''\"\"[0-9]+(?:['!]|/')#", $closure, $w)) { - foreach ($w[0] as $a) { - $v = "'" === substr($a, -1) && "/'" !== substr($a, -2) && $this->specialVarRx - ? preg_split("#([.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?)#", $this->strings[$a], -1, PREG_SPLIT_DELIM_CAPTURE) - : array($this->strings[$a]); - $a = count($v); - - for ($i = 0; $i < $a; ++$i) { - $k = $v[$i]; - - if (1 === $i % 2) { - if (',' === $k[0] || '{' === $k[0]) { - if (':' === substr($k, -1)) { - $k = '.'.substr($k, 1, -1); - } elseif ('get ' === substr($k, 1, 4) || 'set ' === substr($k, 1, 4)) { - ++$this->charFreq[ord($k[1])]; // "g" or "s" - ++$this->charFreq[101]; // "e" - ++$this->charFreq[116]; // "t" - $k = '.'.substr($k, 5); - } else { - $k = substr($k, 1); - } - } elseif (':' === substr($k, -1)) { - $k = substr($k, 0, -1); - } - - $w = &$tree; - - while (isset($w['parent']) && !(isset($w['used'][$k]) || isset($w['local'][$k]))) { - $w = &$w['parent']; - } - - (isset($w['used'][$k]) || isset($w['local'][$k])) && (isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1); - - unset($w); - } - - if (0 === $i % 2 || !isset($vars[$k])) { - foreach (count_chars($v[$i], 1) as $k => $w) { - $this->charFreq[$k] += $w; - } - } - } - } - } - - // Propagate the usage number to parents - - foreach ($vars as $w => $a) { - $k = &$tree; - $chain = array(); - do { - $vars = &$k['local']; - $chain[] = &$k; - if (isset($vars[$w])) { - unset($k['used'][$w]); - if (isset($vars[$w])) { - $vars[$w] += $a; - } else { - $vars[$w] = $a; - } - $a = false; - break; - } - } while ($k['parent'] && $k = &$k['parent']); - - if ($a && !$k['parent']) { - if (isset($vars[$w])) { - $vars[$w] += $a; - } else { - $vars[$w] = $a; - } - } - - if (isset($tree['used'][$w]) && isset($vars[$w])) { - foreach ($chain as &$b) { - isset($b['local'][$w]) || $b['used'][$w] = &$vars[$w]; - } - } - } - - // Analyse childs - - $tree['childs'] = array(); - $vars = &$tree['childs']; - - if (preg_match_all("@//''\"\"#[0-9]+'@", $closure, $w)) { - foreach ($w[0] as $a) { - $vars[$a] = array('parent' => &$tree); - $this->makeVars($this->closures[$a], $vars[$a], $a); - } - } - } - - protected function mergeVarDeclarations($m) - { - return str_replace("\nvar ", ',', $m[0]); - } - - protected function renameVars(&$tree, $root) - { - if ($root) { - $tree['local'] += $tree['used']; - $tree['used'] = array(); - - foreach ($tree['local'] as $k => $v) { - if ('.' == $k[0]) { - $k = substr($k, 1); - } - - if ('true' === $k) { - $this->charFreq[48] += $v; - } elseif ('false' === $k) { - $this->charFreq[49] += $v; - } elseif (!$this->specialVarRx || !preg_match("#^{$this->specialVarRx}$#", $k)) { - foreach (count_chars($k, 1) as $k => $w) { - $this->charFreq[$k] += $w * $v; - } - } elseif (2 == strlen($k)) { - $tree['used'][] = $k[1]; - } - } - - $this->charFreq = $this->rsort($this->charFreq); - - $this->str0 = ''; - $this->str1 = ''; - - foreach ($this->charFreq as $k => $v) { - if (!$v) { - break; - } - - $v = chr($k); - - if ((64 < $k && $k < 91) || (96 < $k && $k < 123)) { // A-Z a-z - $this->str0 .= $v; - $this->str1 .= $v; - } elseif (47 < $k && $k < 58) { // 0-9 - $this->str1 .= $v; - } - } - - if ('' === $this->str0) { - $this->str0 = 'claspemitdbfrugnjvhowkxqyzCLASPEMITDBFRUGNJVHOWKXQYZ'; - $this->str1 = $this->str0.'0123456789'; - } - - foreach ($tree['local'] as $var => $root) { - if ('.' != substr($var, 0, 1) && isset($tree['local'][".{$var}"])) { - $tree['local'][$var] += $tree['local'][".{$var}"]; - } - } - - foreach ($tree['local'] as $var => $root) { - if ('.' == substr($var, 0, 1) && isset($tree['local'][substr($var, 1)])) { - $tree['local'][$var] = $tree['local'][substr($var, 1)]; - } - } - - $tree['local'] = $this->rsort($tree['local']); - - foreach ($tree['local'] as $var => $root) { - switch (substr($var, 0, 1)) { - case '.': - if (!isset($tree['local'][substr($var, 1)])) { - $tree['local'][$var] = '#'.($this->specialVarRx && 3 < strlen($var) && preg_match("'^\.{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : substr($var, 1)); - } - break; - - case ';': $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree); - case '#': break; - - default: - $root = $this->specialVarRx && 2 < strlen($var) && preg_match("'^{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : $var; - $tree['local'][$var] = $root; - if (isset($tree['local'][".{$var}"])) { - $tree['local'][".{$var}"] = '#'.$root; - } - } - } - - foreach ($tree['local'] as $var => $root) { - $tree['local'][$var] = preg_replace("'^#'", '.', $tree['local'][$var]); - } - } else { - $tree['local'] = $this->rsort($tree['local']); - if (false !== $tree['nfe']) { - $tree['used'][] = $tree['local'][$tree['nfe']]; - } - - foreach ($tree['local'] as $var => $root) { - if ($tree['nfe'] !== $var) { - $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree); - } - } - } - - $this->local_tree = &$tree['local']; - $this->used_tree = &$tree['used']; - - $tree['code'] = preg_replace_callback("#[.,{ ]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->varRx}:?#", array($this, 'getNewName'), $tree['code']); - - if ($this->specialVarRx && preg_match_all("#//''\"\"[0-9]+'#", $tree['code'], $b)) { - foreach ($b[0] as $a) { - $this->strings[$a] = preg_replace_callback( - "#[.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?#", - array($this, 'getNewName'), - $this->strings[$a] - ); - } - } - - foreach ($tree['childs'] as $a => &$b) { - $this->renameVars($b, false); - $tree['code'] = str_replace($a, $b['code'], $tree['code']); - unset($tree['childs'][$a]); - } - } - - protected function getNewName($m) - { - $m = $m[0]; - - $pre = '.' === $m[0] ? '.' : ''; - $post = ''; - - if (',' === $m[0] || '{' === $m[0] || ' ' === $m[0]) { - $pre = $m[0]; - - if (':' === substr($m, -1)) { - $post = ':'; - $m = (' ' !== $m[0] ? '.' : '').substr($m, 1, -1); - } elseif ('get ' === substr($m, 1, 4) || 'set ' === substr($m, 1, 4)) { - $pre .= substr($m, 1, 4); - $m = '.'.substr($m, 5); - } else { - $m = substr($m, 1); - } - } elseif (':' === substr($m, -1)) { - $post = ':'; - $m = substr($m, 0, -1); - } - - $post = (isset($this->reserved[$m]) - ? ('true' === $m ? '!0' : ('false' === $m ? '!1' : $m)) - : ( - isset($this->local_tree[$m]) - ? $this->local_tree[$m] - : ( - isset($this->used_tree[$m]) - ? $this->used_tree[$m] - : $m - ) - ) - ).$post; - - return '' === $post ? '' : ($pre.('.' === $post[0] ? substr($post, 1) : $post)); - } - - protected function getNextName(&$tree = array(), &$counter = false) - { - if (false === $counter) { - $counter = &$tree['counter']; - isset($counter) || $counter = -1; - $exclude = array_flip($tree['used']); - } else { - $exclude = $tree; - } - - ++$counter; - - $len0 = strlen($this->str0); - $len1 = strlen($this->str0); - - $name = $this->str0[$counter % $len0]; - - $i = intval($counter / $len0) - 1; - while ($i >= 0) { - $name .= $this->str1[ $i % $len1 ]; - $i = intval($i / $len1) - 1; - } - - return !(isset($this->reserved[$name]) || isset($exclude[$name])) ? $name : $this->getNextName($exclude, $counter); - } - - protected function restoreCc(&$s, $lf = true) - { - $lf && $s = str_replace('@#3', '', $s); - - $s = str_replace('@#1', '@*/', $s); - $s = str_replace('2#@', '//@', $s); - $s = str_replace('1#@', '/*@', $s); - $s = str_replace('##', '#', $s); - } - - private function rsort($array) - { - if (!$array) { - return $array; - } - - $i = 0; - $tuples = array(); - foreach ($array as $k => &$v) { - $tuples[] = array(++$i, $k, &$v); - } - - usort($tuples, function ($a, $b) { - if ($b[2] > $a[2]) { - return 1; - } - if ($b[2] < $a[2]) { - return -1; - } - if ($b[0] > $a[0]) { - return -1; - } - if ($b[0] < $a[0]) { - return 1; - } - - return 0; - }); - - $array = array(); - - foreach ($tuples as $t) { - $array[$t[1]] = &$t[2]; - } - - return $array; - } -} -?>- \ No newline at end of file diff --git a/util/Ldap.class.php b/util/Ldap.class.php @@ -1,182 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Bereitstellen von LDAP-Funktionen. - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Ldap -{ - var $connection; - var $timeout; - var $aliases; - - - /** - * - */ - function Ldap() - { - global $conf; - - $this->timeout = intval($conf['ldap']['search']['timeout']); - - if ( $conf['ldap']['search']['aliases'] ) - $this->aliases = LDAP_DEREF_ALWAYS; - else - $this->aliases = LDAP_DEREF_NEVER; - } - - - - /** - * Verbindung �ffnen. - */ - function connect() - { - global $conf; - - $ldapHost = $conf['ldap']['host']; - $ldapPort = $conf['ldap']['port']; - - // Verbindung zum LDAP-Server herstellen - $this->connection = @ldap_connect( $ldapHost,$ldapPort ); - - // siehe http://bugs.php.net/bug.php?id=15637 - // Unter bestimmten Bedingungen wird trotz nicht erreichbarem LDAP-Server eine PHP-Resource - // zurueck gegeben. Dann erscheint zwar keine Fehlermeldung, aber zumindestens misslingt - // der nachfolgende Bind-Befehl. - if ( !is_resource($this->connection) || $this->connection === false ) - { - Logger::error( "connect to ldap server '$ldapHost:$ldapPort' failed" ); - // Abbruch, wenn LDAP-Server nicht erreichbar - die( "Connection failed to $ldapHost:$ldapPort (".ldap_errno().'/'.ldap_error().'). Please contact your administrator.' ); - } - - // Protokollversion setzen. - $j = ldap_set_option( $this->connection, LDAP_OPT_PROTOCOL_VERSION,intval($conf['ldap']['protocol']) ); - if ( ! $j ) - die( 'LDAP error while setting protocol version'.ldap_errno().'/'.ldap_error().')' ); - - } - - - - /** - * Ein Binding auf den LDAP-Server durchf�hren. - */ - function bind( $user,$pw ) - { - return @ldap_bind( $this->connection,$user,$pw); - } - - - - /** - * Ein Binding auf den LDAP-Server durchf�hren. - */ - function bindAnonymous() - { - return @ldap_bind( $this->connection ); - } - - - - /** - * Das Bindung wird entfernt. - */ - function unbind() - { - ldap_unbind( $this->connection ); - } - - - - /** - * Eine Suche auf den LDAP-Server durchf�hren. - */ - function searchUser( $username ) - { - global $conf; - - $techUser = $conf['ldap']['search']['user']; - $techPass = $conf['ldap']['search']['password']; - - if ( $conf['ldap']['search']['anonymous'] ) - $this->bindAnonymous(); - else - $this->bind( $techUser, $techPass ); - - $dn = $conf['ldap']['search']['basedn']; - $filter = $conf['ldap']['search']['filter']; - $filter = str_replace('{user}', $username, $filter); - - $s = @ldap_search( $this->connection,$dn,$filter,array(),0,1,$this->timeout,$this->aliases ); - - if ( ! is_resource($s) ) - return null; - - $dn = @ldap_get_dn($this->connection, ldap_first_entry($this->connection,$s) ); - - return $dn; - } - - - - /** - * Ein Binding auf den LDAP-Server durchf�hren. - */ - function searchAttribute( $filter,$attr ) - { - global $conf; - - $timeout = intval($conf['ldap']['search']['timeout']); - - if ( $conf['ldap']['search']['aliases'] ) - $aliases = LDAP_DEREF_ALWAYS; - else - $aliases = LDAP_DEREF_NEVER; - - - $base_dn = $conf['ldap']['search']['basedn']; - $s = ldap_search( $this->connection,$base_dn,$filter,array(),0,0,$this->timeout,$this->aliases ); - $ergebnisse = ldap_get_entries($this->connection,$s); - - $liste = array(); -// Html::debug($ergebnisse); - for( $i=0; $i<=$ergebnisse['count']-1; $i++ ) - $liste[] = $ergebnisse[$i][$attr][0]; - - return $liste; - } - - - - /** - * Verbindung schlie�en. - */ - function close() - { - // Verbindung zum LDAP-Server brav beenden - ldap_close( $this->connection ); - } -} - -?>- \ No newline at end of file diff --git a/util/Less.php b/util/Less.php @@ -1,10524 +0,0 @@ -<?php - -/** - * Class for parsing and compiling less files into css - * - * @package Less - * @subpackage parser - * - */ -class Less_Parser{ - - - /** - * Default parser options - */ - public static $default_options = array( - 'compress' => false, // option - whether to compress - 'strictUnits' => false, // whether units need to evaluate correctly - 'strictMath' => false, // whether math has to be within parenthesis - 'relativeUrls' => true, // option - whether to adjust URL's to be relative - 'urlArgs' => '', // whether to add args into url tokens - 'numPrecision' => 8, - - 'import_dirs' => array(), - 'import_callback' => null, - 'cache_dir' => null, - 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback'; - 'cache_callback_get' => null, - 'cache_callback_set' => null, - - 'sourceMap' => false, // whether to output a source map - 'sourceMapBasepath' => null, - 'sourceMapWriteTo' => null, - 'sourceMapURL' => null, - - 'indentation' => ' ', - - 'plugins' => array(), - - ); - - public static $options = array(); - - - private $input; // Less input string - private $input_len; // input string length - private $pos; // current index in `input` - private $saveStack = array(); // holds state for backtracking - private $furthest; - private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding - - /** - * @var Less_Environment - */ - private $env; - - protected $rules = array(); - - private static $imports = array(); - - public static $has_extends = false; - - public static $next_id = 0; - - /** - * Filename to contents of all parsed the files - * - * @var array - */ - public static $contentsMap = array(); - - - /** - * @param Less_Environment|array|null $env - */ - public function __construct( $env = null ){ - - // Top parser on an import tree must be sure there is one "env" - // which will then be passed around by reference. - if( $env instanceof Less_Environment ){ - $this->env = $env; - }else{ - $this->SetOptions(Less_Parser::$default_options); - $this->Reset( $env ); - } - - // mbstring.func_overload > 1 bugfix - // The encoding value must be set for each source file, - // therefore, to conserve resources and improve the speed of this design is taken here - if (ini_get('mbstring.func_overload')) { - $this->mb_internal_encoding = ini_get('mbstring.internal_encoding'); - @ini_set('mbstring.internal_encoding', 'ascii'); - } - - } - - - /** - * Reset the parser state completely - * - */ - public function Reset( $options = null ){ - $this->rules = array(); - self::$imports = array(); - self::$has_extends = false; - self::$imports = array(); - self::$contentsMap = array(); - - $this->env = new Less_Environment($options); - $this->env->Init(); - - //set new options - if( is_array($options) ){ - $this->SetOptions(Less_Parser::$default_options); - $this->SetOptions($options); - } - } - - /** - * Set one or more compiler options - * options: import_dirs, cache_dir, cache_method - * - */ - public function SetOptions( $options ){ - foreach($options as $option => $value){ - $this->SetOption($option,$value); - } - } - - /** - * Set one compiler option - * - */ - public function SetOption($option,$value){ - - switch($option){ - - case 'import_dirs': - $this->SetImportDirs($value); - return; - - case 'cache_dir': - if( is_string($value) ){ - Less_Cache::SetCacheDir($value); - Less_Cache::CheckCacheDir(); - } - return; - } - - Less_Parser::$options[$option] = $value; - } - - /** - * Registers a new custom function - * - * @param string $name function name - * @param callable $callback callback - */ - public function registerFunction($name, $callback) { - $this->env->functions[$name] = $callback; - } - - /** - * Removed an already registered function - * - * @param string $name function name - */ - public function unregisterFunction($name) { - if( isset($this->env->functions[$name]) ) - unset($this->env->functions[$name]); - } - - - /** - * Get the current css buffer - * - * @return string - */ - public function getCss(){ - - $precision = ini_get('precision'); - @ini_set('precision',16); - $locale = setlocale(LC_NUMERIC, 0); - setlocale(LC_NUMERIC, "C"); - - try { - - $root = new Less_Tree_Ruleset(array(), $this->rules ); - $root->root = true; - $root->firstRoot = true; - - - $this->PreVisitors($root); - - self::$has_extends = false; - $evaldRoot = $root->compile($this->env); - - - - $this->PostVisitors($evaldRoot); - - if( Less_Parser::$options['sourceMap'] ){ - $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options ); - // will also save file - // FIXME: should happen somewhere else? - $css = $generator->generateCSS(); - }else{ - $css = $evaldRoot->toCSS(); - } - - if( Less_Parser::$options['compress'] ){ - $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css); - } - - } catch (Exception $exc) { - // Intentional fall-through so we can reset environment - } - - //reset php settings - @ini_set('precision',$precision); - setlocale(LC_NUMERIC, $locale); - - // If you previously defined $this->mb_internal_encoding - // is required to return the encoding as it was before - if ($this->mb_internal_encoding != '') { - @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding); - $this->mb_internal_encoding = ''; - } - - // Rethrow exception after we handled resetting the environment - if (!empty($exc)) { - throw $exc; - } - - - - return $css; - } - - /** - * Run pre-compile visitors - * - */ - private function PreVisitors($root){ - - if( Less_Parser::$options['plugins'] ){ - foreach(Less_Parser::$options['plugins'] as $plugin){ - if( !empty($plugin->isPreEvalVisitor) ){ - $plugin->run($root); - } - } - } - } - - - /** - * Run post-compile visitors - * - */ - private function PostVisitors($evaldRoot){ - - $visitors = array(); - $visitors[] = new Less_Visitor_joinSelector(); - if( self::$has_extends ){ - $visitors[] = new Less_Visitor_processExtends(); - } - $visitors[] = new Less_Visitor_toCSS(); - - - if( Less_Parser::$options['plugins'] ){ - foreach(Less_Parser::$options['plugins'] as $plugin){ - if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){ - continue; - } - - if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){ - array_unshift( $visitors, $plugin); - }else{ - $visitors[] = $plugin; - } - } - } - - - for($i = 0; $i < count($visitors); $i++ ){ - $visitors[$i]->run($evaldRoot); - } - - } - - - /** - * Parse a Less string into css - * - * @param string $str The string to convert - * @param string $uri_root The url of the file - * @return Less_Tree_Ruleset|Less_Parser - */ - public function parse( $str, $file_uri = null ){ - - if( !$file_uri ){ - $uri_root = ''; - $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less'; - }else{ - $file_uri = self::WinPath($file_uri); - $filename = $file_uri; - $uri_root = dirname($file_uri); - } - - $previousFileInfo = $this->env->currentFileInfo; - $uri_root = self::WinPath($uri_root); - $this->SetFileInfo($filename, $uri_root); - - $this->input = $str; - $this->_parse(); - - if( $previousFileInfo ){ - $this->env->currentFileInfo = $previousFileInfo; - } - - return $this; - } - - - /** - * Parse a Less string from a given file - * - * @throws Less_Exception_Parser - * @param string $filename The file to parse - * @param string $uri_root The url of the file - * @param bool $returnRoot Indicates whether the return value should be a css string a root node - * @return Less_Tree_Ruleset|Less_Parser - */ - public function parseFile( $filename, $uri_root = '', $returnRoot = false){ - - if( !file_exists($filename) ){ - $this->Error(sprintf('File `%s` not found.', $filename)); - } - - - // fix uri_root? - // Instead of The mixture of file path for the first argument and directory path for the second argument has bee - if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){ - $uri_root = dirname($uri_root); - } - - - $previousFileInfo = $this->env->currentFileInfo; - - - if( $filename ){ - $filename = self::WinPath(realpath($filename)); - } - $uri_root = self::WinPath($uri_root); - - $this->SetFileInfo($filename, $uri_root); - - self::AddParsedFile($filename); - - if( $returnRoot ){ - $rules = $this->GetRules( $filename ); - $return = new Less_Tree_Ruleset(array(), $rules ); - }else{ - $this->_parse( $filename ); - $return = $this; - } - - if( $previousFileInfo ){ - $this->env->currentFileInfo = $previousFileInfo; - } - - return $return; - } - - - /** - * Allows a user to set variables values - * @param array $vars - * @return Less_Parser - */ - public function ModifyVars( $vars ){ - - $this->input = Less_Parser::serializeVars( $vars ); - $this->_parse(); - - return $this; - } - - - /** - * @param string $filename - */ - public function SetFileInfo( $filename, $uri_root = ''){ - - $filename = Less_Environment::normalizePath($filename); - $dirname = preg_replace('/[^\/\\\\]*$/','',$filename); - - if( !empty($uri_root) ){ - $uri_root = rtrim($uri_root,'/').'/'; - } - - $currentFileInfo = array(); - - //entry info - if( isset($this->env->currentFileInfo) ){ - $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath']; - $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri']; - $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath']; - - }else{ - $currentFileInfo['entryPath'] = $dirname; - $currentFileInfo['entryUri'] = $uri_root; - $currentFileInfo['rootpath'] = $dirname; - } - - $currentFileInfo['currentDirectory'] = $dirname; - $currentFileInfo['currentUri'] = $uri_root.basename($filename); - $currentFileInfo['filename'] = $filename; - $currentFileInfo['uri_root'] = $uri_root; - - - //inherit reference - if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){ - $currentFileInfo['reference'] = true; - } - - $this->env->currentFileInfo = $currentFileInfo; - } - - - /** - * @deprecated 1.5.1.2 - * - */ - public function SetCacheDir( $dir ){ - - if( !file_exists($dir) ){ - if( mkdir($dir) ){ - return true; - } - throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir); - - }elseif( !is_dir($dir) ){ - throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir); - - }elseif( !is_writable($dir) ){ - throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir); - - }else{ - $dir = self::WinPath($dir); - Less_Cache::$cache_dir = rtrim($dir,'/').'/'; - return true; - } - } - - - /** - * Set a list of directories or callbacks the parser should use for determining import paths - * - * @param array $dirs - */ - public function SetImportDirs( $dirs ){ - Less_Parser::$options['import_dirs'] = array(); - - foreach($dirs as $path => $uri_root){ - - $path = self::WinPath($path); - if( !empty($path) ){ - $path = rtrim($path,'/').'/'; - } - - if ( !is_callable($uri_root) ){ - $uri_root = self::WinPath($uri_root); - if( !empty($uri_root) ){ - $uri_root = rtrim($uri_root,'/').'/'; - } - } - - Less_Parser::$options['import_dirs'][$path] = $uri_root; - } - } - - /** - * @param string $file_path - */ - private function _parse( $file_path = null ){ - $this->rules = array_merge($this->rules, $this->GetRules( $file_path )); - } - - - /** - * Return the results of parsePrimary for $file_path - * Use cache and save cached results if possible - * - * @param string|null $file_path - */ - private function GetRules( $file_path ){ - - $this->SetInput($file_path); - - $cache_file = $this->CacheFile( $file_path ); - if( $cache_file ){ - if( Less_Parser::$options['cache_method'] == 'callback' ){ - if( is_callable(Less_Parser::$options['cache_callback_get']) ){ - $cache = call_user_func_array( - Less_Parser::$options['cache_callback_get'], - array($this, $file_path, $cache_file) - ); - - if( $cache ){ - $this->UnsetInput(); - return $cache; - } - } - - }elseif( file_exists($cache_file) ){ - switch(Less_Parser::$options['cache_method']){ - - // Using serialize - // Faster but uses more memory - case 'serialize': - $cache = unserialize(file_get_contents($cache_file)); - if( $cache ){ - touch($cache_file); - $this->UnsetInput(); - return $cache; - } - break; - - - // Using generated php code - case 'var_export': - case 'php': - $this->UnsetInput(); - return include($cache_file); - } - } - } - - $rules = $this->parsePrimary(); - - if( $this->pos < $this->input_len ){ - throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo); - } - - $this->UnsetInput(); - - - //save the cache - if( $cache_file ){ - if( Less_Parser::$options['cache_method'] == 'callback' ){ - if( is_callable(Less_Parser::$options['cache_callback_set']) ){ - call_user_func_array( - Less_Parser::$options['cache_callback_set'], - array($this, $file_path, $cache_file, $rules) - ); - } - - }else{ - //msg('write cache file'); - switch(Less_Parser::$options['cache_method']){ - case 'serialize': - file_put_contents( $cache_file, serialize($rules) ); - break; - case 'php': - file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' ); - break; - case 'var_export': - //Requires __set_state() - file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' ); - break; - } - - Less_Cache::CleanCache(); - } - } - - return $rules; - } - - - /** - * Set up the input buffer - * - */ - public function SetInput( $file_path ){ - - if( $file_path ){ - $this->input = file_get_contents( $file_path ); - } - - $this->pos = $this->furthest = 0; - - // Remove potential UTF Byte Order Mark - $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input); - $this->input_len = strlen($this->input); - - - if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){ - $uri = $this->env->currentFileInfo['currentUri']; - Less_Parser::$contentsMap[$uri] = $this->input; - } - - } - - - /** - * Free up some memory - * - */ - public function UnsetInput(){ - unset($this->input, $this->pos, $this->input_len, $this->furthest); - $this->saveStack = array(); - } - - - public function CacheFile( $file_path ){ - - if( $file_path && $this->CacheEnabled() ){ - - $env = get_object_vars($this->env); - unset($env['frames']); - - $parts = array(); - $parts[] = $file_path; - $parts[] = filesize( $file_path ); - $parts[] = filemtime( $file_path ); - $parts[] = $env; - $parts[] = Less_Version::cache_version; - $parts[] = Less_Parser::$options['cache_method']; - return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache'; - } - } - - - static function AddParsedFile($file){ - self::$imports[] = $file; - } - - static function AllParsedFiles(){ - return self::$imports; - } - - /** - * @param string $file - */ - static function FileParsed($file){ - return in_array($file,self::$imports); - } - - - function save() { - $this->saveStack[] = $this->pos; - } - - private function restore() { - $this->pos = array_pop($this->saveStack); - } - - private function forget(){ - array_pop($this->saveStack); - } - - - private function isWhitespace($offset = 0) { - return preg_match('/\s/',$this->input[ $this->pos + $offset]); - } - - /** - * Parse from a token, regexp or string, and move forward if match - * - * @param array $toks - * @return array - */ - private function match($toks){ - - // The match is confirmed, add the match length to `this::pos`, - // and consume any extra white-space characters (' ' || '\n') - // which come after that. The reason for this is that LeSS's - // grammar is mostly white-space insensitive. - // - - foreach($toks as $tok){ - - $char = $tok[0]; - - if( $char === '/' ){ - $match = $this->MatchReg($tok); - - if( $match ){ - return count($match) === 1 ? $match[0] : $match; - } - - }elseif( $char === '#' ){ - $match = $this->MatchChar($tok[1]); - - }else{ - // Non-terminal, match using a function call - $match = $this->$tok(); - - } - - if( $match ){ - return $match; - } - } - } - - /** - * @param string[] $toks - * - * @return string - */ - private function MatchFuncs($toks){ - - if( $this->pos < $this->input_len ){ - foreach($toks as $tok){ - $match = $this->$tok(); - if( $match ){ - return $match; - } - } - } - - } - - // Match a single character in the input, - private function MatchChar($tok){ - if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){ - $this->skipWhitespace(1); - return $tok; - } - } - - // Match a regexp from the current start point - private function MatchReg($tok){ - - if( preg_match($tok, $this->input, $match, 0, $this->pos) ){ - $this->skipWhitespace(strlen($match[0])); - return $match; - } - } - - - /** - * Same as match(), but don't change the state of the parser, - * just return the match. - * - * @param string $tok - * @return integer - */ - public function PeekReg($tok){ - return preg_match($tok, $this->input, $match, 0, $this->pos); - } - - /** - * @param string $tok - */ - public function PeekChar($tok){ - //return ($this->input[$this->pos] === $tok ); - return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok ); - } - - - /** - * @param integer $length - */ - public function skipWhitespace($length){ - - $this->pos += $length; - - for(; $this->pos < $this->input_len; $this->pos++ ){ - $c = $this->input[$this->pos]; - - if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){ - break; - } - } - } - - - /** - * @param string $tok - * @param string|null $msg - */ - public function expect($tok, $msg = NULL) { - $result = $this->match( array($tok) ); - if (!$result) { - $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg ); - } else { - return $result; - } - } - - /** - * @param string $tok - */ - public function expectChar($tok, $msg = null ){ - $result = $this->MatchChar($tok); - if( !$result ){ - $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg ); - }else{ - return $result; - } - } - - // - // Here in, the parsing rules/functions - // - // The basic structure of the syntax tree generated is as follows: - // - // Ruleset -> Rule -> Value -> Expression -> Entity - // - // Here's some LESS code: - // - // .class { - // color: #fff; - // border: 1px solid #000; - // width: @w + 4px; - // > .child {...} - // } - // - // And here's what the parse tree might look like: - // - // Ruleset (Selector '.class', [ - // Rule ("color", Value ([Expression [Color #fff]])) - // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]])) - // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]])) - // Ruleset (Selector [Element '>', '.child'], [...]) - // ]) - // - // In general, most rules will try to parse a token with the `$()` function, and if the return - // value is truly, will return a new node, of the relevant type. Sometimes, we need to check - // first, before parsing, that's when we use `peek()`. - // - - // - // The `primary` rule is the *entry* and *exit* point of the parser. - // The rules here can appear at any level of the parse tree. - // - // The recursive nature of the grammar is an interplay between the `block` - // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, - // as represented by this simplified grammar: - // - // primary → (ruleset | rule)+ - // ruleset → selector+ block - // block → '{' primary '}' - // - // Only at one point is the primary rule not called from the - // block rule: at the root level. - // - private function parsePrimary(){ - $root = array(); - - while( true ){ - - if( $this->pos >= $this->input_len ){ - break; - } - - $node = $this->parseExtend(true); - if( $node ){ - $root = array_merge($root,$node); - continue; - } - - //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective')); - $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective')); - - if( $node ){ - $root[] = $node; - }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){ - break; - } - - if( $this->PeekChar('}') ){ - break; - } - } - - return $root; - } - - - - // We create a Comment node for CSS comments `/* */`, - // but keep the LeSS comments `//` silent, by just skipping - // over them. - private function parseComment(){ - - if( $this->input[$this->pos] !== '/' ){ - return; - } - - if( $this->input[$this->pos+1] === '/' ){ - $match = $this->MatchReg('/\\G\/\/.*/'); - return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo)); - } - - //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/'); - $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors - if( $comment ){ - return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo)); - } - } - - private function parseComments(){ - $comments = array(); - - while( $this->pos < $this->input_len ){ - $comment = $this->parseComment(); - if( !$comment ){ - break; - } - - $comments[] = $comment; - } - - return $comments; - } - - - - // - // A string, which supports escaping " and ' - // - // "milky way" 'he\'s the one!' - // - private function parseEntitiesQuoted() { - $j = $this->pos; - $e = false; - $index = $this->pos; - - if( $this->input[$this->pos] === '~' ){ - $j++; - $e = true; // Escaped strings - } - - if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){ - return; - } - - if ($e) { - $this->MatchChar('~'); - } - - // Fix for #124: match escaped newlines - //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/'); - $str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/'); - - if( $str ){ - $result = $str[0][0] == '"' ? $str[1] : $str[2]; - return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) ); - } - return; - } - - - // - // A catch-all word, such as: - // - // black border-collapse - // - private function parseEntitiesKeyword(){ - - //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/'); - $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/'); - if( $k ){ - $k = $k[0]; - $color = $this->fromKeyword($k); - if( $color ){ - return $color; - } - return $this->NewObj1('Less_Tree_Keyword',$k); - } - } - - // duplicate of Less_Tree_Color::FromKeyword - private function FromKeyword( $keyword ){ - $keyword = strtolower($keyword); - - if( Less_Colors::hasOwnProperty($keyword) ){ - // detect named color - return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1)); - } - - if( $keyword === 'transparent' ){ - return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true)); - } - } - - // - // A function call - // - // rgb(255, 0, 255) - // - // We also try to catch IE's `alpha()`, but let the `alpha` parser - // deal with the details. - // - // The arguments are parsed with the `entities.arguments` parser. - // - private function parseEntitiesCall(){ - $index = $this->pos; - - if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){ - return; - } - $name = $name[1]; - $nameLC = strtolower($name); - - if ($nameLC === 'url') { - return null; - } - - $this->pos += strlen($name); - - if( $nameLC === 'alpha' ){ - $alpha_ret = $this->parseAlpha(); - if( $alpha_ret ){ - return $alpha_ret; - } - } - - $this->MatchChar('('); // Parse the '(' and consume whitespace. - - $args = $this->parseEntitiesArguments(); - - if( !$this->MatchChar(')') ){ - return; - } - - if ($name) { - return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) ); - } - } - - /** - * Parse a list of arguments - * - * @return array - */ - private function parseEntitiesArguments(){ - - $args = array(); - while( true ){ - $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') ); - if( !$arg ){ - break; - } - - $args[] = $arg; - if( !$this->MatchChar(',') ){ - break; - } - } - return $args; - } - - private function parseEntitiesLiteral(){ - return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') ); - } - - // Assignments are argument entities for calls. - // They are present in ie filter properties as shown below. - // - // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* ) - // - private function parseEntitiesAssignment() { - - $key = $this->MatchReg('/\\G\w+(?=\s?=)/'); - if( !$key ){ - return; - } - - if( !$this->MatchChar('=') ){ - return; - } - - $value = $this->parseEntity(); - if( $value ){ - return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value)); - } - } - - // - // Parse url() tokens - // - // We use a specific rule for urls, because they don't really behave like - // standard function calls. The difference is that the argument doesn't have - // to be enclosed within a string, so it can't be parsed as an Expression. - // - private function parseEntitiesUrl(){ - - - if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){ - return; - } - - $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') ); - if( !$value ){ - $value = ''; - } - - - $this->expectChar(')'); - - - if( isset($value->value) || $value instanceof Less_Tree_Variable ){ - return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo)); - } - - return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) ); - } - - - // - // A Variable entity, such as `@fink`, in - // - // width: @fink + 2px - // - // We use a different parser for variable definitions, - // see `parsers.variable`. - // - private function parseEntitiesVariable(){ - $index = $this->pos; - if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) { - return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo)); - } - } - - - // A variable entity useing the protective {} e.g. @{var} - private function parseEntitiesVariableCurly() { - $index = $this->pos; - - if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){ - return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo)); - } - } - - // - // A Hexadecimal color - // - // #4F3C2F - // - // `rgb` and `hsl` colors are parsed through the `entities.call` parser. - // - private function parseEntitiesColor(){ - if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) { - return $this->NewObj1('Less_Tree_Color',$rgb[1]); - } - } - - // - // A Dimension, that is, a number and a unit - // - // 0.5em 95% - // - private function parseEntitiesDimension(){ - - $c = @ord($this->input[$this->pos]); - - //Is the first char of the dimension 0-9, '.', '+' or '-' - if (($c > 57 || $c < 43) || $c === 47 || $c == 44){ - return; - } - - $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/'); - if( $value ){ - - if( isset($value[2]) ){ - return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2])); - } - return $this->NewObj1('Less_Tree_Dimension',$value[1]); - } - } - - - // - // A unicode descriptor, as is used in unicode-range - // - // U+0?? or U+00A1-00A9 - // - function parseUnicodeDescriptor() { - $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/'); - if( $ud ){ - return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]); - } - } - - - // - // JavaScript code to be evaluated - // - // `window.location.href` - // - private function parseEntitiesJavascript(){ - $e = false; - $j = $this->pos; - if( $this->input[$j] === '~' ){ - $j++; - $e = true; - } - if( $this->input[$j] !== '`' ){ - return; - } - if( $e ){ - $this->MatchChar('~'); - } - $str = $this->MatchReg('/\\G`([^`]*)`/'); - if( $str ){ - return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e)); - } - } - - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink: - // - private function parseVariable(){ - if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) { - return $name[1]; - } - } - - - // - // The variable part of a variable definition. Used in the `rule` parser - // - // @fink(); - // - private function parseRulesetCall(){ - - if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){ - return $this->NewObj1('Less_Tree_RulesetCall', $name[1] ); - } - } - - - // - // extend syntax - used to extend selectors - // - function parseExtend($isRule = false){ - - $index = $this->pos; - $extendList = array(); - - - if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; } - - do{ - $option = null; - $elements = array(); - while( true ){ - $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/'); - if( $option ){ break; } - $e = $this->parseElement(); - if( !$e ){ break; } - $elements[] = $e; - } - - if( $option ){ - $option = $option[1]; - } - - $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index )); - - }while( $this->MatchChar(",") ); - - $this->expect('/\\G\)/'); - - if( $isRule ){ - $this->expect('/\\G;/'); - } - - return $extendList; - } - - - // - // A Mixin call, with an optional argument list - // - // #mixins > .square(#fff); - // .rounded(4px, black); - // .button; - // - // The `while` loop is there because mixins can be - // namespaced, but we only support the child and descendant - // selector for now. - // - private function parseMixinCall(){ - - $char = $this->input[$this->pos]; - if( $char !== '.' && $char !== '#' ){ - return; - } - - $index = $this->pos; - $this->save(); // stop us absorbing part of an invalid selector - - $elements = $this->parseMixinCallElements(); - - if( $elements ){ - - if( $this->MatchChar('(') ){ - $returned = $this->parseMixinArgs(true); - $args = $returned['args']; - $this->expectChar(')'); - }else{ - $args = array(); - } - - $important = $this->parseImportant(); - - if( $this->parseEnd() ){ - $this->forget(); - return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important)); - } - } - - $this->restore(); - } - - - private function parseMixinCallElements(){ - $elements = array(); - $c = null; - - while( true ){ - $elemIndex = $this->pos; - $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/'); - if( !$e ){ - break; - } - $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo)); - $c = $this->MatchChar('>'); - } - - return $elements; - } - - - - /** - * @param boolean $isCall - */ - private function parseMixinArgs( $isCall ){ - $expressions = array(); - $argsSemiColon = array(); - $isSemiColonSeperated = null; - $argsComma = array(); - $expressionContainsNamed = null; - $name = null; - $returner = array('args'=>array(), 'variadic'=> false); - - $this->save(); - - while( true ){ - if( $isCall ){ - $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) ); - } else { - $this->parseComments(); - if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){ - $returner['variadic'] = true; - if( $this->MatchChar(";") && !$isSemiColonSeperated ){ - $isSemiColonSeperated = true; - } - - if( $isSemiColonSeperated ){ - $argsSemiColon[] = array('variadic'=>true); - }else{ - $argsComma[] = array('variadic'=>true); - } - break; - } - $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') ); - } - - if( !$arg ){ - break; - } - - - $nameLoop = null; - if( $arg instanceof Less_Tree_Expression ){ - $arg->throwAwayComments(); - } - $value = $arg; - $val = null; - - if( $isCall ){ - // Variable - if( property_exists($arg,'value') && count($arg->value) == 1 ){ - $val = $arg->value[0]; - } - } else { - $val = $arg; - } - - - if( $val instanceof Less_Tree_Variable ){ - - if( $this->MatchChar(':') ){ - if( $expressions ){ - if( $isSemiColonSeperated ){ - $this->Error('Cannot mix ; and , as delimiter types'); - } - $expressionContainsNamed = true; - } - - // we do not support setting a ruleset as a default variable - it doesn't make sense - // However if we do want to add it, there is nothing blocking it, just don't error - // and remove isCall dependency below - $value = null; - if( $isCall ){ - $value = $this->parseDetachedRuleset(); - } - if( !$value ){ - $value = $this->parseExpression(); - } - - if( !$value ){ - if( $isCall ){ - $this->Error('could not understand value for named argument'); - } else { - $this->restore(); - $returner['args'] = array(); - return $returner; - } - } - - $nameLoop = ($name = $val->name); - }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){ - $returner['variadic'] = true; - if( $this->MatchChar(";") && !$isSemiColonSeperated ){ - $isSemiColonSeperated = true; - } - if( $isSemiColonSeperated ){ - $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true); - }else{ - $argsComma[] = array('name'=> $arg->name, 'variadic' => true); - } - break; - }elseif( !$isCall ){ - $name = $nameLoop = $val->name; - $value = null; - } - } - - if( $value ){ - $expressions[] = $value; - } - - $argsComma[] = array('name'=>$nameLoop, 'value'=>$value ); - - if( $this->MatchChar(',') ){ - continue; - } - - if( $this->MatchChar(';') || $isSemiColonSeperated ){ - - if( $expressionContainsNamed ){ - $this->Error('Cannot mix ; and , as delimiter types'); - } - - $isSemiColonSeperated = true; - - if( count($expressions) > 1 ){ - $value = $this->NewObj1('Less_Tree_Value', $expressions); - } - $argsSemiColon[] = array('name'=>$name, 'value'=>$value ); - - $name = null; - $expressions = array(); - $expressionContainsNamed = false; - } - } - - $this->forget(); - $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma); - return $returner; - } - - - - // - // A Mixin definition, with a list of parameters - // - // .rounded (@radius: 2px, @color) { - // ... - // } - // - // Until we have a finer grained state-machine, we have to - // do a look-ahead, to make sure we don't have a mixin call. - // See the `rule` function for more information. - // - // We start by matching `.rounded (`, and then proceed on to - // the argument list, which has optional default values. - // We store the parameters in `params`, with a `value` key, - // if there is a value, such as in the case of `@radius`. - // - // Once we've got our params list, and a closing `)`, we parse - // the `{...}` block. - // - private function parseMixinDefinition(){ - $cond = null; - - $char = $this->input[$this->pos]; - if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){ - return; - } - - $this->save(); - - $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/'); - if( $match ){ - $name = $match[1]; - - $argInfo = $this->parseMixinArgs( false ); - $params = $argInfo['args']; - $variadic = $argInfo['variadic']; - - - // .mixincall("@{a}"); - // looks a bit like a mixin definition.. - // also - // .mixincall(@a: {rule: set;}); - // so we have to be nice and restore - if( !$this->MatchChar(')') ){ - $this->furthest = $this->pos; - $this->restore(); - return; - } - - - $this->parseComments(); - - if ($this->MatchReg('/\\Gwhen/')) { // Guard - $cond = $this->expect('parseConditions', 'Expected conditions'); - } - - $ruleset = $this->parseBlock(); - - if( is_array($ruleset) ){ - $this->forget(); - return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic)); - } - - $this->restore(); - }else{ - $this->forget(); - } - } - - // - // Entities are the smallest recognized token, - // and can be found inside a rule's value. - // - private function parseEntity(){ - - return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') ); - } - - // - // A Rule terminator. Note that we use `peek()` to check for '}', - // because the `block` rule will be expecting it, but we still need to make sure - // it's there, if ';' was ommitted. - // - private function parseEnd(){ - return $this->MatchChar(';') || $this->PeekChar('}'); - } - - // - // IE's alpha function - // - // alpha(opacity=88) - // - private function parseAlpha(){ - - if ( ! $this->MatchReg('/\\G\(opacity=/i')) { - return; - } - - $value = $this->MatchReg('/\\G[0-9]+/'); - if( $value ){ - $value = $value[0]; - }else{ - $value = $this->parseEntitiesVariable(); - if( !$value ){ - return; - } - } - - $this->expectChar(')'); - return $this->NewObj1('Less_Tree_Alpha',$value); - } - - - // - // A Selector Element - // - // div - // + h1 - // #socks - // input[type="text"] - // - // Elements are the building blocks for Selectors, - // they are made out of a `Combinator` (see combinator rule), - // and an element name, such as a tag a class, or `*`. - // - private function parseElement(){ - $c = $this->parseCombinator(); - $index = $this->pos; - - $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/', - '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') ); - - if( is_null($e) ){ - $this->save(); - if( $this->MatchChar('(') ){ - if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){ - $e = $this->NewObj1('Less_Tree_Paren',$v); - $this->forget(); - }else{ - $this->restore(); - } - }else{ - $this->forget(); - } - } - - if( !is_null($e) ){ - return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo)); - } - } - - // - // Combinators combine elements together, in a Selector. - // - // Because our parser isn't white-space sensitive, special care - // has to be taken, when parsing the descendant combinator, ` `, - // as it's an empty space. We have to check the previous character - // in the input, to see if it's a ` ` character. - // - private function parseCombinator(){ - if( $this->pos < $this->input_len ){ - $c = $this->input[$this->pos]; - if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){ - - $this->pos++; - if( $this->input[$this->pos] === '^' ){ - $c = '^^'; - $this->pos++; - } - - $this->skipWhitespace(0); - - return $c; - } - - if( $this->pos > 0 && $this->isWhitespace(-1) ){ - return ' '; - } - } - } - - // - // A CSS selector (see selector below) - // with less extensions e.g. the ability to extend and guard - // - private function parseLessSelector(){ - return $this->parseSelector(true); - } - - // - // A CSS Selector - // - // .class > div + h1 - // li a:hover - // - // Selectors are made out of one or more Elements, see above. - // - private function parseSelector( $isLess = false ){ - $elements = array(); - $extendList = array(); - $condition = null; - $when = false; - $extend = false; - $e = null; - $c = null; - $index = $this->pos; - - while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){ - if( $when ){ - $condition = $this->expect('parseConditions', 'expected condition'); - }elseif( $condition ){ - //error("CSS guard can only be used at the end of selector"); - }elseif( $extend ){ - $extendList = array_merge($extendList,$extend); - }else{ - //if( count($extendList) ){ - //error("Extend can only be used at the end of selector"); - //} - if( $this->pos < $this->input_len ){ - $c = $this->input[ $this->pos ]; - } - $elements[] = $e; - $e = null; - } - - if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; } - } - - if( $elements ){ - return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo)); - } - if( $extendList ) { - $this->Error('Extend must be used to extend a selector, it cannot be used on its own'); - } - } - - private function parseTag(){ - return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*'); - } - - private function parseAttribute(){ - - $val = null; - - if( !$this->MatchChar('[') ){ - return; - } - - $key = $this->parseEntitiesVariableCurly(); - if( !$key ){ - $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/'); - } - - $op = $this->MatchReg('/\\G[|~*$^]?=/'); - if( $op ){ - $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') ); - } - - $this->expectChar(']'); - - return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val)); - } - - // - // The `block` rule is used by `ruleset` and `mixin.definition`. - // It's a wrapper around the `primary` rule, with added `{}`. - // - private function parseBlock(){ - if( $this->MatchChar('{') ){ - $content = $this->parsePrimary(); - if( $this->MatchChar('}') ){ - return $content; - } - } - } - - private function parseBlockRuleset(){ - $block = $this->parseBlock(); - - if( $block ){ - $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block)); - } - - return $block; - } - - private function parseDetachedRuleset(){ - $blockRuleset = $this->parseBlockRuleset(); - if( $blockRuleset ){ - return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset); - } - } - - // - // div, .class, body > p {...} - // - private function parseRuleset(){ - $selectors = array(); - - $this->save(); - - while( true ){ - $s = $this->parseLessSelector(); - if( !$s ){ - break; - } - $selectors[] = $s; - $this->parseComments(); - - if( $s->condition && count($selectors) > 1 ){ - $this->Error('Guards are only currently allowed on a single selector.'); - } - - if( !$this->MatchChar(',') ){ - break; - } - if( $s->condition ){ - $this->Error('Guards are only currently allowed on a single selector.'); - } - $this->parseComments(); - } - - - if( $selectors ){ - $rules = $this->parseBlock(); - if( is_array($rules) ){ - $this->forget(); - return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports - } - } - - // Backtrack - $this->furthest = $this->pos; - $this->restore(); - } - - /** - * Custom less.php parse function for finding simple name-value css pairs - * ex: width:100px; - * - */ - private function parseNameValue(){ - - $index = $this->pos; - $this->save(); - - - //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/'); - $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/'); - if( $match ){ - - if( $match[4] == '}' ){ - $this->pos = $index + strlen($match[0])-1; - } - - if( $match[3] ){ - $match[2] .= ' !important'; - } - - return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo)); - } - - $this->restore(); - } - - - private function parseRule( $tryAnonymous = null ){ - - $merge = false; - $startOfRule = $this->pos; - - $c = $this->input[$this->pos]; - if( $c === '.' || $c === '#' || $c === '&' ){ - return; - } - - $this->save(); - $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty')); - - if( $name ){ - - $isVariable = is_string($name); - - $value = null; - if( $isVariable ){ - $value = $this->parseDetachedRuleset(); - } - - $important = null; - if( !$value ){ - - // prefer to try to parse first if its a variable or we are compressing - // but always fallback on the other one - //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){ - if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){ - $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue')); - }else{ - $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue')); - } - - $important = $this->parseImportant(); - - // a name returned by this.ruleProperty() is always an array of the form: - // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"] - // where each item is a tree.Keyword or tree.Variable - if( !$isVariable && is_array($name) ){ - $nm = array_pop($name); - if( $nm->value ){ - $merge = $nm->value; - } - } - } - - - if( $value && $this->parseEnd() ){ - $this->forget(); - return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo)); - }else{ - $this->furthest = $this->pos; - $this->restore(); - if( $value && !$tryAnonymous ){ - return $this->parseRule(true); - } - } - }else{ - $this->forget(); - } - } - - function parseAnonymousValue(){ - - if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){ - $this->pos += strlen($match[1]); - return $this->NewObj1('Less_Tree_Anonymous',$match[1]); - } - } - - // - // An @import directive - // - // @import "lib"; - // - // Depending on our environment, importing is done differently: - // In the browser, it's an XHR request, in Node, it would be a - // file-system operation. The function used for importing is - // stored in `import`, which we pass to the Import constructor. - // - private function parseImport(){ - - $this->save(); - - $dir = $this->MatchReg('/\\G@import?\s+/'); - - if( $dir ){ - $options = $this->parseImportOptions(); - $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl')); - - if( $path ){ - $features = $this->parseMediaFeatures(); - if( $this->MatchChar(';') ){ - if( $features ){ - $features = $this->NewObj1('Less_Tree_Value',$features); - } - - $this->forget(); - return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo)); - } - } - } - - $this->restore(); - } - - private function parseImportOptions(){ - - $options = array(); - - // list of options, surrounded by parens - if( !$this->MatchChar('(') ){ - return $options; - } - do{ - $optionName = $this->parseImportOption(); - if( $optionName ){ - $value = true; - switch( $optionName ){ - case "css": - $optionName = "less"; - $value = false; - break; - case "once": - $optionName = "multiple"; - $value = false; - break; - } - $options[$optionName] = $value; - if( !$this->MatchChar(',') ){ break; } - } - }while( $optionName ); - $this->expectChar(')'); - return $options; - } - - private function parseImportOption(){ - $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/'); - if( $opt ){ - return $opt[1]; - } - } - - private function parseMediaFeature() { - $nodes = array(); - - do{ - $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable')); - if( $e ){ - $nodes[] = $e; - } elseif ($this->MatchChar('(')) { - $p = $this->parseProperty(); - $e = $this->parseValue(); - if ($this->MatchChar(')')) { - if ($p && $e) { - $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true)); - $nodes[] = $this->NewObj1('Less_Tree_Paren',$r); - } elseif ($e) { - $nodes[] = $this->NewObj1('Less_Tree_Paren',$e); - } else { - return null; - } - } else - return null; - } - } while ($e); - - if ($nodes) { - return $this->NewObj1('Less_Tree_Expression',$nodes); - } - } - - private function parseMediaFeatures() { - $features = array(); - - do{ - $e = $this->parseMediaFeature(); - if( $e ){ - $features[] = $e; - if (!$this->MatchChar(',')) break; - }else{ - $e = $this->parseEntitiesVariable(); - if( $e ){ - $features[] = $e; - if (!$this->MatchChar(',')) break; - } - } - } while ($e); - - return $features ? $features : null; - } - - private function parseMedia() { - if( $this->MatchReg('/\\G@media/') ){ - $features = $this->parseMediaFeatures(); - $rules = $this->parseBlock(); - - if( is_array($rules) ){ - return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo)); - } - } - } - - - // - // A CSS Directive - // - // @charset "utf-8"; - // - private function parseDirective(){ - - if( !$this->PeekChar('@') ){ - return; - } - - $rules = null; - $index = $this->pos; - $hasBlock = true; - $hasIdentifier = false; - $hasExpression = false; - $hasUnknown = false; - - - $value = $this->MatchFuncs(array('parseImport','parseMedia')); - if( $value ){ - return $value; - } - - $this->save(); - - $name = $this->MatchReg('/\\G@[a-z-]+/'); - - if( !$name ) return; - $name = $name[0]; - - - $nonVendorSpecificName = $name; - $pos = strpos($name,'-', 2); - if( $name[1] == '-' && $pos > 0 ){ - $nonVendorSpecificName = "@" . substr($name, $pos + 1); - } - - - switch( $nonVendorSpecificName ){ - /* - case "@font-face": - case "@viewport": - case "@top-left": - case "@top-left-corner": - case "@top-center": - case "@top-right": - case "@top-right-corner": - case "@bottom-left": - case "@bottom-left-corner": - case "@bottom-center": - case "@bottom-right": - case "@bottom-right-corner": - case "@left-top": - case "@left-middle": - case "@left-bottom": - case "@right-top": - case "@right-middle": - case "@right-bottom": - hasBlock = true; - break; - */ - case "@charset": - $hasIdentifier = true; - $hasBlock = false; - break; - case "@namespace": - $hasExpression = true; - $hasBlock = false; - break; - case "@keyframes": - $hasIdentifier = true; - break; - case "@host": - case "@page": - case "@document": - case "@supports": - $hasUnknown = true; - break; - } - - if( $hasIdentifier ){ - $value = $this->parseEntity(); - if( !$value ){ - $this->error("expected " . $name . " identifier"); - } - } else if( $hasExpression ){ - $value = $this->parseExpression(); - if( !$value ){ - $this->error("expected " . $name. " expression"); - } - } else if ($hasUnknown) { - - $value = $this->MatchReg('/\\G[^{;]+/'); - if( $value ){ - $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0])); - } - } - - if( $hasBlock ){ - $rules = $this->parseBlockRuleset(); - } - - if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) { - $this->forget(); - return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo)); - } - - $this->restore(); - } - - - // - // A Value is a comma-delimited list of Expressions - // - // font-family: Baskerville, Georgia, serif; - // - // In a Rule, a Value represents everything after the `:`, - // and before the `;`. - // - private function parseValue(){ - $expressions = array(); - - do{ - $e = $this->parseExpression(); - if( $e ){ - $expressions[] = $e; - if (! $this->MatchChar(',')) { - break; - } - } - }while($e); - - if( $expressions ){ - return $this->NewObj1('Less_Tree_Value',$expressions); - } - } - - private function parseImportant (){ - if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){ - return ' !important'; - } - } - - private function parseSub (){ - - if( $this->MatchChar('(') ){ - $a = $this->parseAddition(); - if( $a ){ - $this->expectChar(')'); - return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached - } - } - } - - - /** - * Parses multiplication operation - * - * @return Less_Tree_Operation|null - */ - function parseMultiplication(){ - - $return = $m = $this->parseOperand(); - if( $return ){ - while( true ){ - - $isSpaced = $this->isWhitespace( -1 ); - - if( $this->PeekReg('/\\G\/[*\/]/') ){ - break; - } - - $op = $this->MatchChar('/'); - if( !$op ){ - $op = $this->MatchChar('*'); - if( !$op ){ - break; - } - } - - $a = $this->parseOperand(); - - if(!$a) { break; } - - $m->parensInOp = true; - $a->parensInOp = true; - $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) ); - } - } - return $return; - - } - - - /** - * Parses an addition operation - * - * @return Less_Tree_Operation|null - */ - private function parseAddition (){ - - $return = $m = $this->parseMultiplication(); - if( $return ){ - while( true ){ - - $isSpaced = $this->isWhitespace( -1 ); - - $op = $this->MatchReg('/\\G[-+]\s+/'); - if( $op ){ - $op = $op[0]; - }else{ - if( !$isSpaced ){ - $op = $this->match(array('#+','#-')); - } - if( !$op ){ - break; - } - } - - $a = $this->parseMultiplication(); - if( !$a ){ - break; - } - - $m->parensInOp = true; - $a->parensInOp = true; - $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced)); - } - } - - return $return; - } - - - /** - * Parses the conditions - * - * @return Less_Tree_Condition|null - */ - private function parseConditions() { - $index = $this->pos; - $return = $a = $this->parseCondition(); - if( $a ){ - while( true ){ - if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){ - break; - } - $b = $this->parseCondition(); - if( !$b ){ - break; - } - - $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index)); - } - return $return; - } - } - - private function parseCondition() { - $index = $this->pos; - $negate = false; - $c = null; - - if ($this->MatchReg('/\\Gnot/')) $negate = true; - $this->expectChar('('); - $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); - - if( $a ){ - $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/'); - if( $op ){ - $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted')); - if( $b ){ - $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate)); - } else { - $this->Error('Unexpected expression'); - } - } else { - $k = $this->NewObj1('Less_Tree_Keyword','true'); - $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate)); - } - $this->expectChar(')'); - return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c; - } - } - - /** - * An operand is anything that can be part of an operation, - * such as a Color, or a Variable - * - */ - private function parseOperand (){ - - $negate = false; - $offset = $this->pos+1; - if( $offset >= $this->input_len ){ - return; - } - $char = $this->input[$offset]; - if( $char === '@' || $char === '(' ){ - $negate = $this->MatchChar('-'); - } - - $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall')); - - if( $negate ){ - $o->parensInOp = true; - $o = $this->NewObj1('Less_Tree_Negative',$o); - } - - return $o; - } - - - /** - * Expressions either represent mathematical operations, - * or white-space delimited Entities. - * - * 1px solid black - * @var * 2 - * - * @return Less_Tree_Expression|null - */ - private function parseExpression (){ - $entities = array(); - - do{ - $e = $this->MatchFuncs(array('parseAddition','parseEntity')); - if( $e ){ - $entities[] = $e; - // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here - if( !$this->PeekReg('/\\G\/[\/*]/') ){ - $delim = $this->MatchChar('/'); - if( $delim ){ - $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim); - } - } - } - }while($e); - - if( $entities ){ - return $this->NewObj1('Less_Tree_Expression',$entities); - } - } - - - /** - * Parse a property - * eg: 'min-width', 'orientation', etc - * - * @return string - */ - private function parseProperty (){ - $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/'); - if( $name ){ - return $name[1]; - } - } - - - /** - * Parse a rule property - * eg: 'color', 'width', 'height', etc - * - * @return string - */ - private function parseRuleProperty(){ - $offset = $this->pos; - $name = array(); - $index = array(); - $length = 0; - - - $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name ); - while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // ! - - if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){ - // at last, we have the complete match now. move forward, - // convert name particles to tree objects and return: - $this->skipWhitespace($length); - - if( $name[0] === '' ){ - array_shift($name); - array_shift($index); - } - foreach($name as $k => $s ){ - if( !$s || $s[0] !== '@' ){ - $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s); - }else{ - $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo)); - } - } - return $name; - } - - - } - - private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){ - preg_match($re, $this->input, $a, 0, $offset); - if( $a ){ - $index[] = $this->pos + $length; - $length += strlen($a[0]); - $offset += strlen($a[0]); - $name[] = $a[1]; - return true; - } - } - - public static function serializeVars( $vars ){ - $s = ''; - - foreach($vars as $name => $value){ - $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';'); - } - - return $s; - } - - - /** - * Some versions of php have trouble with method_exists($a,$b) if $a is not an object - * - * @param string $b - */ - public static function is_method($a,$b){ - return is_object($a) && method_exists($a,$b); - } - - - /** - * Round numbers similarly to javascript - * eg: 1.499999 to 1 instead of 2 - * - */ - public static function round($i, $precision = 0){ - - $precision = pow(10,$precision); - $i = $i*$precision; - - $ceil = ceil($i); - $floor = floor($i); - if( ($ceil - $i) <= ($i - $floor) ){ - return $ceil/$precision; - }else{ - return $floor/$precision; - } - } - - - /** - * Create Less_Tree_* objects and optionally generate a cache string - * - * @return mixed - */ - public function NewObj0($class){ - $obj = new $class(); - if( $this->CacheEnabled() ){ - $obj->cache_string = ' new '.$class.'()'; - } - return $obj; - } - - public function NewObj1($class, $arg){ - $obj = new $class( $arg ); - if( $this->CacheEnabled() ){ - $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')'; - } - return $obj; - } - - public function NewObj2($class, $args){ - $obj = new $class( $args[0], $args[1] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - public function NewObj3($class, $args){ - $obj = new $class( $args[0], $args[1], $args[2] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - public function NewObj4($class, $args){ - $obj = new $class( $args[0], $args[1], $args[2], $args[3] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - public function NewObj5($class, $args){ - $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - public function NewObj6($class, $args){ - $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - public function NewObj7($class, $args){ - $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] ); - if( $this->CacheEnabled() ){ - $this->ObjCache( $obj, $class, $args); - } - return $obj; - } - - //caching - public function ObjCache($obj, $class, $args=array()){ - $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')'; - } - - public function ArgCache($args){ - return implode(',',array_map( array('Less_Parser','ArgString'),$args)); - } - - - /** - * Convert an argument to a string for use in the parser cache - * - * @return string - */ - public static function ArgString($arg){ - - $type = gettype($arg); - - if( $type === 'object'){ - $string = $arg->cache_string; - unset($arg->cache_string); - return $string; - - }elseif( $type === 'array' ){ - $string = ' Array('; - foreach($arg as $k => $a){ - $string .= var_export($k,true).' => '.self::ArgString($a).','; - } - return $string . ')'; - } - - return var_export($arg,true); - } - - public function Error($msg){ - throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo); - } - - public static function WinPath($path){ - return str_replace('\\', '/', $path); - } - - public function CacheEnabled(){ - return false; - //return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback'))); - } - -} - - -/** - * Utility for css colors - * - * @package Less - * @subpackage color - */ -class Less_Colors { - - public static $colors = array( - 'aliceblue'=>'#f0f8ff', - 'antiquewhite'=>'#faebd7', - 'aqua'=>'#00ffff', - 'aquamarine'=>'#7fffd4', - 'azure'=>'#f0ffff', - 'beige'=>'#f5f5dc', - 'bisque'=>'#ffe4c4', - 'black'=>'#000000', - 'blanchedalmond'=>'#ffebcd', - 'blue'=>'#0000ff', - 'blueviolet'=>'#8a2be2', - 'brown'=>'#a52a2a', - 'burlywood'=>'#deb887', - 'cadetblue'=>'#5f9ea0', - 'chartreuse'=>'#7fff00', - 'chocolate'=>'#d2691e', - 'coral'=>'#ff7f50', - 'cornflowerblue'=>'#6495ed', - 'cornsilk'=>'#fff8dc', - 'crimson'=>'#dc143c', - 'cyan'=>'#00ffff', - 'darkblue'=>'#00008b', - 'darkcyan'=>'#008b8b', - 'darkgoldenrod'=>'#b8860b', - 'darkgray'=>'#a9a9a9', - 'darkgrey'=>'#a9a9a9', - 'darkgreen'=>'#006400', - 'darkkhaki'=>'#bdb76b', - 'darkmagenta'=>'#8b008b', - 'darkolivegreen'=>'#556b2f', - 'darkorange'=>'#ff8c00', - 'darkorchid'=>'#9932cc', - 'darkred'=>'#8b0000', - 'darksalmon'=>'#e9967a', - 'darkseagreen'=>'#8fbc8f', - 'darkslateblue'=>'#483d8b', - 'darkslategray'=>'#2f4f4f', - 'darkslategrey'=>'#2f4f4f', - 'darkturquoise'=>'#00ced1', - 'darkviolet'=>'#9400d3', - 'deeppink'=>'#ff1493', - 'deepskyblue'=>'#00bfff', - 'dimgray'=>'#696969', - 'dimgrey'=>'#696969', - 'dodgerblue'=>'#1e90ff', - 'firebrick'=>'#b22222', - 'floralwhite'=>'#fffaf0', - 'forestgreen'=>'#228b22', - 'fuchsia'=>'#ff00ff', - 'gainsboro'=>'#dcdcdc', - 'ghostwhite'=>'#f8f8ff', - 'gold'=>'#ffd700', - 'goldenrod'=>'#daa520', - 'gray'=>'#808080', - 'grey'=>'#808080', - 'green'=>'#008000', - 'greenyellow'=>'#adff2f', - 'honeydew'=>'#f0fff0', - 'hotpink'=>'#ff69b4', - 'indianred'=>'#cd5c5c', - 'indigo'=>'#4b0082', - 'ivory'=>'#fffff0', - 'khaki'=>'#f0e68c', - 'lavender'=>'#e6e6fa', - 'lavenderblush'=>'#fff0f5', - 'lawngreen'=>'#7cfc00', - 'lemonchiffon'=>'#fffacd', - 'lightblue'=>'#add8e6', - 'lightcoral'=>'#f08080', - 'lightcyan'=>'#e0ffff', - 'lightgoldenrodyellow'=>'#fafad2', - 'lightgray'=>'#d3d3d3', - 'lightgrey'=>'#d3d3d3', - 'lightgreen'=>'#90ee90', - 'lightpink'=>'#ffb6c1', - 'lightsalmon'=>'#ffa07a', - 'lightseagreen'=>'#20b2aa', - 'lightskyblue'=>'#87cefa', - 'lightslategray'=>'#778899', - 'lightslategrey'=>'#778899', - 'lightsteelblue'=>'#b0c4de', - 'lightyellow'=>'#ffffe0', - 'lime'=>'#00ff00', - 'limegreen'=>'#32cd32', - 'linen'=>'#faf0e6', - 'magenta'=>'#ff00ff', - 'maroon'=>'#800000', - 'mediumaquamarine'=>'#66cdaa', - 'mediumblue'=>'#0000cd', - 'mediumorchid'=>'#ba55d3', - 'mediumpurple'=>'#9370d8', - 'mediumseagreen'=>'#3cb371', - 'mediumslateblue'=>'#7b68ee', - 'mediumspringgreen'=>'#00fa9a', - 'mediumturquoise'=>'#48d1cc', - 'mediumvioletred'=>'#c71585', - 'midnightblue'=>'#191970', - 'mintcream'=>'#f5fffa', - 'mistyrose'=>'#ffe4e1', - 'moccasin'=>'#ffe4b5', - 'navajowhite'=>'#ffdead', - 'navy'=>'#000080', - 'oldlace'=>'#fdf5e6', - 'olive'=>'#808000', - 'olivedrab'=>'#6b8e23', - 'orange'=>'#ffa500', - 'orangered'=>'#ff4500', - 'orchid'=>'#da70d6', - 'palegoldenrod'=>'#eee8aa', - 'palegreen'=>'#98fb98', - 'paleturquoise'=>'#afeeee', - 'palevioletred'=>'#d87093', - 'papayawhip'=>'#ffefd5', - 'peachpuff'=>'#ffdab9', - 'peru'=>'#cd853f', - 'pink'=>'#ffc0cb', - 'plum'=>'#dda0dd', - 'powderblue'=>'#b0e0e6', - 'purple'=>'#800080', - 'red'=>'#ff0000', - 'rosybrown'=>'#bc8f8f', - 'royalblue'=>'#4169e1', - 'saddlebrown'=>'#8b4513', - 'salmon'=>'#fa8072', - 'sandybrown'=>'#f4a460', - 'seagreen'=>'#2e8b57', - 'seashell'=>'#fff5ee', - 'sienna'=>'#a0522d', - 'silver'=>'#c0c0c0', - 'skyblue'=>'#87ceeb', - 'slateblue'=>'#6a5acd', - 'slategray'=>'#708090', - 'slategrey'=>'#708090', - 'snow'=>'#fffafa', - 'springgreen'=>'#00ff7f', - 'steelblue'=>'#4682b4', - 'tan'=>'#d2b48c', - 'teal'=>'#008080', - 'thistle'=>'#d8bfd8', - 'tomato'=>'#ff6347', - 'turquoise'=>'#40e0d0', - 'violet'=>'#ee82ee', - 'wheat'=>'#f5deb3', - 'white'=>'#ffffff', - 'whitesmoke'=>'#f5f5f5', - 'yellow'=>'#ffff00', - 'yellowgreen'=>'#9acd32' - ); - - public static function hasOwnProperty($color) { - return isset(self::$colors[$color]); - } - - - public static function color($color) { - return self::$colors[$color]; - } - -} - - - -/** - * Environment - * - * @package Less - * @subpackage environment - */ -class Less_Environment{ - - //public $paths = array(); // option - unmodified - paths to search for imports on - //public static $files = array(); // list of files that have been imported, used for import-once - //public $rootpath; // option - rootpath to append to URL's - //public static $strictImports = null; // option - - //public $insecure; // option - whether to allow imports from insecure ssl hosts - //public $processImports; // option - whether to process imports. if false then imports will not be imported - //public $javascriptEnabled; // option - whether JavaScript is enabled. if undefined, defaults to true - //public $useFileCache; // browser only - whether to use the per file session cache - public $currentFileInfo; // information about the current file - for error reporting and importing and making urls relative etc. - - public $importMultiple = false; // whether we are currently importing multiple copies - - - /** - * @var array - */ - public $frames = array(); - - /** - * @var array - */ - public $mediaBlocks = array(); - - /** - * @var array - */ - public $mediaPath = array(); - - public static $parensStack = 0; - - public static $tabLevel = 0; - - public static $lastRule = false; - - public static $_outputMap; - - public static $mixin_stack = 0; - - /** - * @var array - */ - public $functions = array(); - - - public function Init(){ - - self::$parensStack = 0; - self::$tabLevel = 0; - self::$lastRule = false; - self::$mixin_stack = 0; - - if( Less_Parser::$options['compress'] ){ - - Less_Environment::$_outputMap = array( - ',' => ',', - ': ' => ':', - '' => '', - ' ' => ' ', - ':' => ' :', - '+' => '+', - '~' => '~', - '>' => '>', - '|' => '|', - '^' => '^', - '^^' => '^^' - ); - - }else{ - - Less_Environment::$_outputMap = array( - ',' => ', ', - ': ' => ': ', - '' => '', - ' ' => ' ', - ':' => ' :', - '+' => ' + ', - '~' => ' ~ ', - '>' => ' > ', - '|' => '|', - '^' => ' ^ ', - '^^' => ' ^^ ' - ); - - } - } - - - public function copyEvalEnv($frames = array() ){ - $new_env = new Less_Environment(); - $new_env->frames = $frames; - return $new_env; - } - - - public static function isMathOn(){ - return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack; - } - - public static function isPathRelative($path){ - return !preg_match('/^(?:[a-z-]+:|\/)/',$path); - } - - - /** - * Canonicalize a path by resolving references to '/./', '/../' - * Does not remove leading "../" - * @param string path or url - * @return string Canonicalized path - * - */ - public static function normalizePath($path){ - - $segments = explode('/',$path); - $segments = array_reverse($segments); - - $path = array(); - $path_len = 0; - - while( $segments ){ - $segment = array_pop($segments); - switch( $segment ) { - - case '.': - break; - - case '..': - if( !$path_len || ( $path[$path_len-1] === '..') ){ - $path[] = $segment; - $path_len++; - }else{ - array_pop($path); - $path_len--; - } - break; - - default: - $path[] = $segment; - $path_len++; - break; - } - } - - return implode('/',$path); - } - - - public function unshiftFrame($frame){ - array_unshift($this->frames, $frame); - } - - public function shiftFrame(){ - return array_shift($this->frames); - } - -} - - -/** - * Builtin functions - * - * @package Less - * @subpackage function - * @see http://lesscss.org/functions/ - */ -class Less_Functions{ - - public $env; - public $currentFileInfo; - - function __construct($env, $currentFileInfo = null ){ - $this->env = $env; - $this->currentFileInfo = $currentFileInfo; - } - - /** - * @param string $op - */ - public static function operate( $op, $a, $b ){ - switch ($op) { - case '+': return $a + $b; - case '-': return $a - $b; - case '*': return $a * $b; - case '/': return $a / $b; - } - } - - public static function clamp($val, $max = 1){ - return min( max($val, 0), $max); - } - - public static function fround( $value ){ - - if( $value === 0 ){ - return $value; - } - - if( Less_Parser::$options['numPrecision'] ){ - $p = pow(10, Less_Parser::$options['numPrecision']); - return round( $value * $p) / $p; - } - return $value; - } - - public static function number($n){ - - if ($n instanceof Less_Tree_Dimension) { - return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value); - } else if (is_numeric($n)) { - return $n; - } else { - throw new Less_Exception_Compiler("color functions take numbers as parameters"); - } - } - - public static function scaled($n, $size = 255 ){ - if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){ - return (float)$n->value * $size / 100; - } else { - return Less_Functions::number($n); - } - } - - public function rgb ($r = null, $g = null, $b = null){ - if (is_null($r) || is_null($g) || is_null($b)) { - throw new Less_Exception_Compiler("rgb expects three parameters"); - } - return $this->rgba($r, $g, $b, 1.0); - } - - public function rgba($r = null, $g = null, $b = null, $a = null){ - $rgb = array($r, $g, $b); - $rgb = array_map(array('Less_Functions','scaled'),$rgb); - - $a = self::number($a); - return new Less_Tree_Color($rgb, $a); - } - - public function hsl($h, $s, $l){ - return $this->hsla($h, $s, $l, 1.0); - } - - public function hsla($h, $s, $l, $a){ - - $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int - $s = self::clamp(self::number($s)); - $l = self::clamp(self::number($l)); - $a = self::clamp(self::number($a)); - - $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s; - - $m1 = $l * 2 - $m2; - - return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255, - self::hsla_hue($h, $m1, $m2) * 255, - self::hsla_hue($h - 1/3, $m1, $m2) * 255, - $a); - } - - /** - * @param double $h - */ - public function hsla_hue($h, $m1, $m2){ - $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h); - if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6; - else if ($h * 2 < 1) return $m2; - else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6; - else return $m1; - } - - public function hsv($h, $s, $v) { - return $this->hsva($h, $s, $v, 1.0); - } - - /** - * @param double $a - */ - public function hsva($h, $s, $v, $a) { - $h = ((Less_Functions::number($h) % 360) / 360 ) * 360; - $s = Less_Functions::number($s); - $v = Less_Functions::number($v); - $a = Less_Functions::number($a); - - $i = floor(($h / 60) % 6); - $f = ($h / 60) - $i; - - $vs = array( $v, - $v * (1 - $s), - $v * (1 - $f * $s), - $v * (1 - (1 - $f) * $s)); - - $perm = array(array(0, 3, 1), - array(2, 0, 1), - array(1, 0, 3), - array(1, 2, 0), - array(3, 1, 0), - array(0, 1, 2)); - - return $this->rgba($vs[$perm[$i][0]] * 255, - $vs[$perm[$i][1]] * 255, - $vs[$perm[$i][2]] * 255, - $a); - } - - public function hue($color = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $c = $color->toHSL(); - return new Less_Tree_Dimension(Less_Parser::round($c['h'])); - } - - public function saturation($color = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $c = $color->toHSL(); - return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%'); - } - - public function lightness($color = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $c = $color->toHSL(); - return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%'); - } - - public function hsvhue( $color = null ){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) ); - } - - - public function hsvsaturation( $color = null ){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' ); - } - - public function hsvvalue( $color = null ){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsv = $color->toHSV(); - return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' ); - } - - public function red($color = null) { - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return new Less_Tree_Dimension( $color->rgb[0] ); - } - - public function green($color = null) { - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return new Less_Tree_Dimension( $color->rgb[1] ); - } - - public function blue($color = null) { - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return new Less_Tree_Dimension( $color->rgb[2] ); - } - - public function alpha($color = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $c = $color->toHSL(); - return new Less_Tree_Dimension($c['a']); - } - - public function luma ($color = null) { - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%'); - } - - public function luminance( $color = null ){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $luminance = - (0.2126 * $color->rgb[0] / 255) - + (0.7152 * $color->rgb[1] / 255) - + (0.0722 * $color->rgb[2] / 255); - - return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%'); - } - - public function saturate($color = null, $amount = null){ - // filter: saturate(3.2); - // should be kept as is, so check for color - if ($color instanceof Less_Tree_Dimension) { - return null; - } - - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - - $hsl['s'] += $amount->value / 100; - $hsl['s'] = self::clamp($hsl['s']); - - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - /** - * @param Less_Tree_Dimension $amount - */ - public function desaturate($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - - $hsl['s'] -= $amount->value / 100; - $hsl['s'] = self::clamp($hsl['s']); - - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - - - public function lighten($color = null, $amount=null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - - $hsl['l'] += $amount->value / 100; - $hsl['l'] = self::clamp($hsl['l']); - - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - public function darken($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - $hsl['l'] -= $amount->value / 100; - $hsl['l'] = self::clamp($hsl['l']); - - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - public function fadein($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - $hsl['a'] += $amount->value / 100; - $hsl['a'] = self::clamp($hsl['a']); - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - public function fadeout($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - $hsl['a'] -= $amount->value / 100; - $hsl['a'] = self::clamp($hsl['a']); - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - public function fade($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - - $hsl['a'] = $amount->value / 100; - $hsl['a'] = self::clamp($hsl['a']); - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - - - public function spin($color = null, $amount = null){ - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$amount instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $hsl = $color->toHSL(); - $hue = fmod($hsl['h'] + $amount->value, 360); - - $hsl['h'] = $hue < 0 ? 360 + $hue : $hue; - - return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']); - } - - // - // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein - // http://sass-lang.com - // - - /** - * @param Less_Tree_Color $color1 - */ - public function mix($color1 = null, $color2 = null, $weight = null){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$weight) { - $weight = new Less_Tree_Dimension('50', '%'); - } - if (!$weight instanceof Less_Tree_Dimension) { - throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - $p = $weight->value / 100.0; - $w = $p * 2 - 1; - $hsl1 = $color1->toHSL(); - $hsl2 = $color2->toHSL(); - $a = $hsl1['a'] - $hsl2['a']; - - $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2; - $w2 = 1 - $w1; - - $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2, - $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2, - $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2); - - $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p); - - return new Less_Tree_Color($rgb, $alpha); - } - - public function greyscale($color){ - return $this->desaturate($color, new Less_Tree_Dimension(100,'%')); - } - - - public function contrast( $color, $dark = null, $light = null, $threshold = null){ - // filter: contrast(3.2); - // should be kept as is, so check for color - if (!$color instanceof Less_Tree_Color) { - return null; - } - if( !$light ){ - $light = $this->rgba(255, 255, 255, 1.0); - } - if( !$dark ){ - $dark = $this->rgba(0, 0, 0, 1.0); - } - - if (!$dark instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$light instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - //Figure out which is actually light and dark! - if( $dark->luma() > $light->luma() ){ - $t = $light; - $light = $dark; - $dark = $t; - } - if( !$threshold ){ - $threshold = 0.43; - } else { - $threshold = Less_Functions::number($threshold); - } - - if( $color->luma() < $threshold ){ - return $light; - } else { - return $dark; - } - } - - public function e ($str){ - if( is_string($str) ){ - return new Less_Tree_Anonymous($str); - } - return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value); - } - - public function escape ($str){ - - $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$'); - - return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert)); - } - - - /** - * todo: This function will need some additional work to make it work the same as less.js - * - */ - public function replace( $string, $pattern, $replacement, $flags = null ){ - $result = $string->value; - - $expr = '/'.str_replace('/','\\/',$pattern->value).'/'; - if( $flags && $flags->value){ - $expr .= self::replace_flags($flags->value); - } - - $result = preg_replace($expr,$replacement->value,$result); - - - if( property_exists($string,'quote') ){ - return new Less_Tree_Quoted( $string->quote, $result, $string->escaped); - } - return new Less_Tree_Quoted( '', $result ); - } - - public static function replace_flags($flags){ - $flags = str_split($flags,1); - $new_flags = ''; - - foreach($flags as $flag){ - switch($flag){ - case 'e': - case 'g': - break; - - default: - $new_flags .= $flag; - break; - } - } - - return $new_flags; - } - - public function _percent(){ - $string = func_get_arg(0); - - $args = func_get_args(); - array_shift($args); - $result = $string->value; - - foreach($args as $arg){ - if( preg_match('/%[sda]/i',$result, $token) ){ - $token = $token[0]; - $value = stristr($token, 's') ? $arg->value : $arg->toCSS(); - $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value; - $result = preg_replace('/%[sda]/i',$value, $result, 1); - } - } - $result = str_replace('%%', '%', $result); - - return new Less_Tree_Quoted( $string->quote , $result, $string->escaped); - } - - public function unit( $val, $unit = null) { - if( !($val instanceof Less_Tree_Dimension) ){ - throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') ); - } - - if( $unit ){ - if( $unit instanceof Less_Tree_Keyword ){ - $unit = $unit->value; - } else { - $unit = $unit->toCSS(); - } - } else { - $unit = ""; - } - return new Less_Tree_Dimension($val->value, $unit ); - } - - public function convert($val, $unit){ - return $val->convertTo($unit->value); - } - - public function round($n, $f = false) { - - $fraction = 0; - if( $f !== false ){ - $fraction = $f->value; - } - - return $this->_math('Less_Parser::round',null, $n, $fraction); - } - - public function pi(){ - return new Less_Tree_Dimension(M_PI); - } - - public function mod($a, $b) { - return new Less_Tree_Dimension( $a->value % $b->value, $a->unit); - } - - - - public function pow($x, $y) { - if( is_numeric($x) && is_numeric($y) ){ - $x = new Less_Tree_Dimension($x); - $y = new Less_Tree_Dimension($y); - }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){ - throw new Less_Exception_Compiler('Arguments must be numbers'); - } - - return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit ); - } - - // var mathFunctions = [{name:"ce ... - public function ceil( $n ){ return $this->_math('ceil', null, $n); } - public function floor( $n ){ return $this->_math('floor', null, $n); } - public function sqrt( $n ){ return $this->_math('sqrt', null, $n); } - public function abs( $n ){ return $this->_math('abs', null, $n); } - - public function tan( $n ){ return $this->_math('tan', '', $n); } - public function sin( $n ){ return $this->_math('sin', '', $n); } - public function cos( $n ){ return $this->_math('cos', '', $n); } - - public function atan( $n ){ return $this->_math('atan', 'rad', $n); } - public function asin( $n ){ return $this->_math('asin', 'rad', $n); } - public function acos( $n ){ return $this->_math('acos', 'rad', $n); } - - private function _math() { - $args = func_get_args(); - $fn = array_shift($args); - $unit = array_shift($args); - - if ($args[0] instanceof Less_Tree_Dimension) { - - if( $unit === null ){ - $unit = $args[0]->unit; - }else{ - $args[0] = $args[0]->unify(); - } - $args[0] = (float)$args[0]->value; - return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit); - } else if (is_numeric($args[0])) { - return call_user_func_array($fn,$args); - } else { - throw new Less_Exception_Compiler("math functions take numbers as parameters"); - } - } - - /** - * @param boolean $isMin - */ - private function _minmax( $isMin, $args ){ - - $arg_count = count($args); - - if( $arg_count < 1 ){ - throw new Less_Exception_Compiler( 'one or more arguments required'); - } - - $j = null; - $unitClone = null; - $unitStatic = null; - - - $order = array(); // elems only contains original argument values. - $values = array(); // key is the unit.toString() for unified tree.Dimension values, - // value is the index into the order array. - - - for( $i = 0; $i < $arg_count; $i++ ){ - $current = $args[$i]; - if( !($current instanceof Less_Tree_Dimension) ){ - if( is_array($args[$i]->value) ){ - $args[] = $args[$i]->value; - } - continue; - } - - if( $current->unit->toString() === '' && !$unitClone ){ - $temp = new Less_Tree_Dimension($current->value, $unitClone); - $currentUnified = $temp->unify(); - }else{ - $currentUnified = $current->unify(); - } - - if( $currentUnified->unit->toString() === "" && !$unitStatic ){ - $unit = $unitStatic; - }else{ - $unit = $currentUnified->unit->toString(); - } - - if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){ - $unitStatic = $unit; - } - - if( $unit != '' && !$unitClone ){ - $unitClone = $current->unit->toString(); - } - - if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){ - $j = $values['']; - }elseif( isset($values[$unit]) ){ - $j = $values[$unit]; - }else{ - - if( $unitStatic && $unit !== $unitStatic ){ - throw new Less_Exception_Compiler( 'incompatible types'); - } - $values[$unit] = count($order); - $order[] = $current; - continue; - } - - - if( $order[$j]->unit->toString() === "" && $unitClone ){ - $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone); - $referenceUnified = $temp->unify(); - }else{ - $referenceUnified = $order[$j]->unify(); - } - if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){ - $order[$j] = $current; - } - } - - if( count($order) == 1 ){ - return $order[0]; - } - $args = array(); - foreach($order as $a){ - $args[] = $a->toCSS($this->env); - } - return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')'); - } - - public function min(){ - $args = func_get_args(); - return $this->_minmax( true, $args ); - } - - public function max(){ - $args = func_get_args(); - return $this->_minmax( false, $args ); - } - - public function getunit($n){ - return new Less_Tree_Anonymous($n->unit); - } - - public function argb($color) { - if (!$color instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return new Less_Tree_Anonymous($color->toARGB()); - } - - public function percentage($n) { - return new Less_Tree_Dimension($n->value * 100, '%'); - } - - public function color($n) { - - if( $n instanceof Less_Tree_Quoted ){ - $colorCandidate = $n->value; - $returnColor = Less_Tree_Color::fromKeyword($colorCandidate); - if( $returnColor ){ - return $returnColor; - } - if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){ - return new Less_Tree_Color(substr($colorCandidate, 1)); - } - throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF"); - } else { - throw new Less_Exception_Compiler("argument must be a string"); - } - } - - - public function iscolor($n) { - return $this->_isa($n, 'Less_Tree_Color'); - } - - public function isnumber($n) { - return $this->_isa($n, 'Less_Tree_Dimension'); - } - - public function isstring($n) { - return $this->_isa($n, 'Less_Tree_Quoted'); - } - - public function iskeyword($n) { - return $this->_isa($n, 'Less_Tree_Keyword'); - } - - public function isurl($n) { - return $this->_isa($n, 'Less_Tree_Url'); - } - - public function ispixel($n) { - return $this->isunit($n, 'px'); - } - - public function ispercentage($n) { - return $this->isunit($n, '%'); - } - - public function isem($n) { - return $this->isunit($n, 'em'); - } - - /** - * @param string $unit - */ - public function isunit( $n, $unit ){ - return ($n instanceof Less_Tree_Dimension) && $n->unit->is( ( property_exists($unit,'value') ? $unit->value : $unit) ) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); - } - - /** - * @param string $type - */ - private function _isa($n, $type) { - return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); - } - - public function tint($color, $amount) { - return $this->mix( $this->rgb(255,255,255), $color, $amount); - } - - public function shade($color, $amount) { - return $this->mix($this->rgb(0, 0, 0), $color, $amount); - } - - public function extract($values, $index ){ - $index = (int)$index->value - 1; // (1-based index) - // handle non-array values as an array of length 1 - // return 'undefined' if index is invalid - if( property_exists($values,'value') && is_array($values->value) ){ - if( isset($values->value[$index]) ){ - return $values->value[$index]; - } - return null; - - }elseif( (int)$index === 0 ){ - return $values; - } - - return null; - } - - public function length($values){ - $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1; - return new Less_Tree_Dimension($n); - } - - public function datauri($mimetypeNode, $filePathNode = null ) { - - $filePath = ( $filePathNode ? $filePathNode->value : null ); - $mimetype = $mimetypeNode->value; - - $args = 2; - if( !$filePath ){ - $filePath = $mimetype; - $args = 1; - } - - $filePath = str_replace('\\','/',$filePath); - if( Less_Environment::isPathRelative($filePath) ){ - - if( Less_Parser::$options['relativeUrls'] ){ - $temp = $this->currentFileInfo['currentDirectory']; - } else { - $temp = $this->currentFileInfo['entryPath']; - } - - if( !empty($temp) ){ - $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath); - } - - } - - - // detect the mimetype if not given - if( $args < 2 ){ - - /* incomplete - $mime = require('mime'); - mimetype = mime.lookup(path); - - // use base 64 unless it's an ASCII or UTF-8 format - var charset = mime.charsets.lookup(mimetype); - useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0; - if (useBase64) mimetype += ';base64'; - */ - - $mimetype = Less_Mime::lookup($filePath); - - $charset = Less_Mime::charsets_lookup($mimetype); - $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8')); - if( $useBase64 ){ $mimetype .= ';base64'; } - - }else{ - $useBase64 = preg_match('/;base64$/',$mimetype); - } - - - if( file_exists($filePath) ){ - $buf = @file_get_contents($filePath); - }else{ - $buf = false; - } - - - // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded - // and the --ieCompat flag is enabled, return a normal url() instead. - $DATA_URI_MAX_KB = 32; - $fileSizeInKB = round( strlen($buf) / 1024 ); - if( $fileSizeInKB >= $DATA_URI_MAX_KB ){ - $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo); - return $url->compile($this); - } - - if( $buf ){ - $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf); - $filePath = '"data:' . $mimetype . ',' . $buf . '"'; - } - - return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) ); - } - - //svg-gradient - public function svggradient( $direction ){ - - $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]'; - $arguments = func_get_args(); - - if( count($arguments) < 3 ){ - throw new Less_Exception_Compiler( $throw_message ); - } - - $stops = array_slice($arguments,1); - $gradientType = 'linear'; - $rectangleDimension = 'x="0" y="0" width="1" height="1"'; - $useBase64 = true; - $directionValue = $direction->toCSS(); - - - switch( $directionValue ){ - case "to bottom": - $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"'; - break; - case "to right": - $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"'; - break; - case "to bottom right": - $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"'; - break; - case "to top right": - $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"'; - break; - case "ellipse": - case "ellipse at center": - $gradientType = "radial"; - $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"'; - $rectangleDimension = 'x="-50" y="-50" width="101" height="101"'; - break; - default: - throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" ); - } - - $returner = '<?xml version="1.0" ?>' . - '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' . - '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>'; - - for( $i = 0; $i < count($stops); $i++ ){ - if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){ - $color = $stops[$i]->value[0]; - $position = $stops[$i]->value[1]; - }else{ - $color = $stops[$i]; - $position = null; - } - - if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){ - throw new Less_Exception_Compiler( $throw_message ); - } - if( $position ){ - $positionValue = $position->toCSS(); - }elseif( $i === 0 ){ - $positionValue = '0%'; - }else{ - $positionValue = '100%'; - } - $alpha = $color->alpha; - $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>'; - } - - $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>'; - - - if( $useBase64 ){ - $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'"; - }else{ - $returner = "'data:image/svg+xml,".$returner."'"; - } - - return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) ); - } - - - /** - * Php version of javascript's `encodeURIComponent` function - * - * @param string $string The string to encode - * @return string The encoded string - */ - public static function encodeURIComponent($string){ - $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')'); - return strtr(rawurlencode($string), $revert); - } - - - // Color Blending - // ref: http://www.w3.org/TR/compositing-1 - - public function colorBlend( $mode, $color1, $color2 ){ - $ab = $color1->alpha; // backdrop - $as = $color2->alpha; // source - $r = array(); // result - - $ar = $as + $ab * (1 - $as); - for( $i = 0; $i < 3; $i++ ){ - $cb = $color1->rgb[$i] / 255; - $cs = $color2->rgb[$i] / 255; - $cr = call_user_func( $mode, $cb, $cs ); - if( $ar ){ - $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar; - } - $r[$i] = $cr * 255; - } - - return new Less_Tree_Color($r, $ar); - } - - public function multiply($color1 = null, $color2 = null ){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 ); - } - - private function colorBlendMultiply($cb, $cs){ - return $cb * $cs; - } - - public function screen($color1 = null, $color2 = null ){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 ); - } - - private function colorBlendScreen( $cb, $cs){ - return $cb + $cs - $cb * $cs; - } - - public function overlay($color1 = null, $color2 = null){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 ); - } - - private function colorBlendOverlay($cb, $cs ){ - $cb *= 2; - return ($cb <= 1) - ? $this->colorBlendMultiply($cb, $cs) - : $this->colorBlendScreen($cb - 1, $cs); - } - - public function softlight($color1 = null, $color2 = null){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 ); - } - - private function colorBlendSoftlight($cb, $cs ){ - $d = 1; - $e = $cb; - if( $cs > 0.5 ){ - $e = 1; - $d = ($cb > 0.25) ? sqrt($cb) - : ((16 * $cb - 12) * $cb + 4) * $cb; - } - return $cb - (1 - 2 * $cs) * $e * ($d - $cb); - } - - public function hardlight($color1 = null, $color2 = null){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 ); - } - - private function colorBlendHardlight( $cb, $cs ){ - return $this->colorBlendOverlay($cs, $cb); - } - - public function difference($color1 = null, $color2 = null) { - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 ); - } - - private function colorBlendDifference( $cb, $cs ){ - return abs($cb - $cs); - } - - public function exclusion( $color1 = null, $color2 = null ){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 ); - } - - private function colorBlendExclusion( $cb, $cs ){ - return $cb + $cs - 2 * $cb * $cs; - } - - public function average($color1 = null, $color2 = null){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 ); - } - - // non-w3c functions: - public function colorBlendAverage($cb, $cs ){ - return ($cb + $cs) / 2; - } - - public function negation($color1 = null, $color2 = null ){ - if (!$color1 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - if (!$color2 instanceof Less_Tree_Color) { - throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') ); - } - - return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 ); - } - - public function colorBlendNegation($cb, $cs){ - return 1 - abs($cb + $cs - 1); - } - - // ~ End of Color Blending - -} - - -/** - * Mime lookup - * - * @package Less - * @subpackage node - */ -class Less_Mime{ - - // this map is intentionally incomplete - // if you want more, install 'mime' dep - static $_types = array( - '.htm' => 'text/html', - '.html'=> 'text/html', - '.gif' => 'image/gif', - '.jpg' => 'image/jpeg', - '.jpeg'=> 'image/jpeg', - '.png' => 'image/png', - '.ttf' => 'application/x-font-ttf', - '.otf' => 'application/x-font-otf', - '.eot' => 'application/vnd.ms-fontobject', - '.woff' => 'application/x-font-woff', - '.svg' => 'image/svg+xml', - ); - - public static function lookup( $filepath ){ - $parts = explode('.',$filepath); - $ext = '.'.strtolower(array_pop($parts)); - - if( !isset(self::$_types[$ext]) ){ - return null; - } - return self::$_types[$ext]; - } - - public static function charsets_lookup( $type = null ){ - // assumes all text types are UTF-8 - return $type && preg_match('/^text\//',$type) ? 'UTF-8' : ''; - } -} - - -/** - * Tree - * - * @package Less - * @subpackage tree - */ -class Less_Tree{ - - public $cache_string; - - public function toCSS(){ - $output = new Less_Output(); - $this->genCSS($output); - return $output->toString(); - } - - - /** - * Generate CSS by adding it to the output object - * - * @param Less_Output $output The output - * @return void - */ - public function genCSS($output){} - - - /** - * @param Less_Tree_Ruleset[] $rules - */ - public static function outputRuleset( $output, $rules ){ - - $ruleCnt = count($rules); - Less_Environment::$tabLevel++; - - - // Compressed - if( Less_Parser::$options['compress'] ){ - $output->add('{'); - for( $i = 0; $i < $ruleCnt; $i++ ){ - $rules[$i]->genCSS( $output ); - } - - $output->add( '}' ); - Less_Environment::$tabLevel--; - return; - } - - - // Non-compressed - $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 ); - $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation']; - - $output->add( " {" ); - for($i = 0; $i < $ruleCnt; $i++ ){ - $output->add( $tabRuleStr ); - $rules[$i]->genCSS( $output ); - } - Less_Environment::$tabLevel--; - $output->add( $tabSetStr.'}' ); - - } - - public function accept($visitor){} - - - public static function ReferencedArray($rules){ - foreach($rules as $rule){ - if( method_exists($rule, 'markReferenced') ){ - $rule->markReferenced(); - } - } - } - - - /** - * Requires php 5.3+ - */ - public static function __set_state($args){ - - $class = get_called_class(); - $obj = new $class(null,null,null,null); - foreach($args as $key => $val){ - $obj->$key = $val; - } - return $obj; - } - -} - - -/** - * Parser output - * - * @package Less - * @subpackage output - */ -class Less_Output{ - - /** - * Output holder - * - * @var string - */ - protected $strs = array(); - - /** - * Adds a chunk to the stack - * - * @param string $chunk The chunk to output - * @param Less_FileInfo $fileInfo The file information - * @param integer $index The index - * @param mixed $mapLines - */ - public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){ - $this->strs[] = $chunk; - } - - /** - * Is the output empty? - * - * @return boolean - */ - public function isEmpty(){ - return count($this->strs) === 0; - } - - - /** - * Converts the output to string - * - * @return string - */ - public function toString(){ - return implode('',$this->strs); - } - -} - -/** - * Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_Visitor{ - - protected $methods = array(); - protected $_visitFnCache = array(); - - public function __construct(){ - $this->_visitFnCache = get_class_methods(get_class($this)); - $this->_visitFnCache = array_flip($this->_visitFnCache); - } - - public function visitObj( $node ){ - - $funcName = 'visit'.$node->type; - if( isset($this->_visitFnCache[$funcName]) ){ - - $visitDeeper = true; - $this->$funcName( $node, $visitDeeper ); - - if( $visitDeeper ){ - $node->accept($this); - } - - $funcName = $funcName . "Out"; - if( isset($this->_visitFnCache[$funcName]) ){ - $this->$funcName( $node ); - } - - }else{ - $node->accept($this); - } - - return $node; - } - - public function visitArray( $nodes ){ - - array_map( array($this,'visitObj'), $nodes); - return $nodes; - } -} - - - -/** - * Replacing Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_VisitorReplacing extends Less_Visitor{ - - public function visitObj( $node ){ - - $funcName = 'visit'.$node->type; - if( isset($this->_visitFnCache[$funcName]) ){ - - $visitDeeper = true; - $node = $this->$funcName( $node, $visitDeeper ); - - if( $node ){ - if( $visitDeeper && is_object($node) ){ - $node->accept($this); - } - - $funcName = $funcName . "Out"; - if( isset($this->_visitFnCache[$funcName]) ){ - $this->$funcName( $node ); - } - } - - }else{ - $node->accept($this); - } - - return $node; - } - - public function visitArray( $nodes ){ - - $newNodes = array(); - foreach($nodes as $node){ - $evald = $this->visitObj($node); - if( $evald ){ - if( is_array($evald) ){ - self::flatten($evald,$newNodes); - }else{ - $newNodes[] = $evald; - } - } - } - return $newNodes; - } - - public function flatten( $arr, &$out ){ - - foreach($arr as $item){ - if( !is_array($item) ){ - $out[] = $item; - continue; - } - - foreach($item as $nestedItem){ - if( is_array($nestedItem) ){ - self::flatten( $nestedItem, $out); - }else{ - $out[] = $nestedItem; - } - } - } - - return $out; - } - -} - - - - -/** - * Configurable - * - * @package Less - * @subpackage Core - */ -abstract class Less_Configurable { - - /** - * Array of options - * - * @var array - */ - protected $options = array(); - - /** - * Array of default options - * - * @var array - */ - protected $defaultOptions = array(); - - - /** - * Set options - * - * If $options is an object it will be converted into an array by called - * it's toArray method. - * - * @throws Exception - * @param array|object $options - * - */ - public function setOptions($options){ - $options = array_intersect_key($options,$this->defaultOptions); - $this->options = array_merge($this->defaultOptions, $this->options, $options); - } - - - /** - * Get an option value by name - * - * If the option is empty or not set a NULL value will be returned. - * - * @param string $name - * @param mixed $default Default value if confiuration of $name is not present - * @return mixed - */ - public function getOption($name, $default = null){ - if(isset($this->options[$name])){ - return $this->options[$name]; - } - return $default; - } - - - /** - * Set an option - * - * @param string $name - * @param mixed $value - */ - public function setOption($name, $value){ - $this->options[$name] = $value; - } - -} - -/** - * Alpha - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Alpha extends Less_Tree{ - public $value; - public $type = 'Alpha'; - - public function __construct($val){ - $this->value = $val; - } - - //function accept( $visitor ){ - // $this->value = $visitor->visit( $this->value ); - //} - - public function compile($env){ - - if( is_object($this->value) ){ - $this->value = $this->value->compile($env); - } - - return $this; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - $output->add( "alpha(opacity=" ); - - if( is_string($this->value) ){ - $output->add( $this->value ); - }else{ - $this->value->genCSS( $output); - } - - $output->add( ')' ); - } - - public function toCSS(){ - return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")"; - } - - -} - -/** - * Anonymous - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Anonymous extends Less_Tree{ - public $value; - public $quote; - public $index; - public $mapLines; - public $currentFileInfo; - public $type = 'Anonymous'; - - /** - * @param integer $index - * @param boolean $mapLines - */ - public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){ - $this->value = $value; - $this->index = $index; - $this->mapLines = $mapLines; - $this->currentFileInfo = $currentFileInfo; - } - - public function compile(){ - return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines); - } - - public function compare($x){ - if( !is_object($x) ){ - return -1; - } - - $left = $this->toCSS(); - $right = $x->toCSS(); - - if( $left === $right ){ - return 0; - } - - return $left < $right ? -1 : 1; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines ); - } - - public function toCSS(){ - return $this->value; - } - -} - - -/** - * Assignment - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Assignment extends Less_Tree{ - - public $key; - public $value; - public $type = 'Assignment'; - - public function __construct($key, $val) { - $this->key = $key; - $this->value = $val; - } - - public function accept( $visitor ){ - $this->value = $visitor->visitObj( $this->value ); - } - - public function compile($env) { - return new Less_Tree_Assignment( $this->key, $this->value->compile($env)); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->key . '=' ); - $this->value->genCSS( $output ); - } - - public function toCss(){ - return $this->key . '=' . $this->value->toCSS(); - } -} - - -/** - * Attribute - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Attribute extends Less_Tree{ - - public $key; - public $op; - public $value; - public $type = 'Attribute'; - - public function __construct($key, $op, $value){ - $this->key = $key; - $this->op = $op; - $this->value = $value; - } - - public function compile($env){ - - $key_obj = is_object($this->key); - $val_obj = is_object($this->value); - - if( !$key_obj && !$val_obj ){ - return $this; - } - - return new Less_Tree_Attribute( - $key_obj ? $this->key->compile($env) : $this->key , - $this->op, - $val_obj ? $this->value->compile($env) : $this->value); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->toCSS() ); - } - - public function toCSS(){ - $value = $this->key; - - if( $this->op ){ - $value .= $this->op; - $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value); - } - - return '[' . $value . ']'; - } -} - - -/** - * Call - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Call extends Less_Tree{ - public $value; - - protected $name; - protected $args; - protected $index; - protected $currentFileInfo; - public $type = 'Call'; - - public function __construct($name, $args, $index, $currentFileInfo = null ){ - $this->name = $name; - $this->args = $args; - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - } - - public function accept( $visitor ){ - $this->args = $visitor->visitArray( $this->args ); - } - - // - // When evaluating a function call, - // we either find the function in `tree.functions` [1], - // in which case we call it, passing the evaluated arguments, - // or we simply print it out as it appeared originally [2]. - // - // The *functions.js* file contains the built-in functions. - // - // The reason why we evaluate the arguments, is in the case where - // we try to pass a variable to a function, like: `saturate(@color)`. - // The function should receive the value, not the variable. - // - public function compile($env=null){ - $args = array(); - foreach($this->args as $a){ - $args[] = $a->compile($env); - } - - $nameLC = strtolower($this->name); - switch($nameLC){ - case '%': - $nameLC = '_percent'; - break; - - case 'get-unit': - $nameLC = 'getunit'; - break; - - case 'data-uri': - $nameLC = 'datauri'; - break; - - case 'svg-gradient': - $nameLC = 'svggradient'; - break; - } - - $result = null; - if( $nameLC === 'default' ){ - $result = Less_Tree_DefaultFunc::compile(); - - }else{ - - if( method_exists('Less_Functions',$nameLC) ){ // 1. - try { - - $func = new Less_Functions($env, $this->currentFileInfo); - $result = call_user_func_array( array($func,$nameLC),$args); - - } catch (Exception $e) { - throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index); - } - } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) { - try { - $result = call_user_func_array( $env->functions[$nameLC], $args ); - } catch (Exception $e) { - throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index); - } - } - } - - if( $result !== null ){ - return $result; - } - - - return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo ); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - $output->add( $this->name . '(', $this->currentFileInfo, $this->index ); - $args_len = count($this->args); - for($i = 0; $i < $args_len; $i++ ){ - $this->args[$i]->genCSS( $output ); - if( $i + 1 < $args_len ){ - $output->add( ', ' ); - } - } - - $output->add( ')' ); - } - - - //public function toCSS(){ - // return $this->compile()->toCSS(); - //} - -} - - -/** - * Color - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Color extends Less_Tree{ - public $rgb; - public $alpha; - public $isTransparentKeyword; - public $type = 'Color'; - - public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){ - - if( $isTransparentKeyword ){ - $this->rgb = $rgb; - $this->alpha = $a; - $this->isTransparentKeyword = true; - return; - } - - $this->rgb = array(); - if( is_array($rgb) ){ - $this->rgb = $rgb; - }else if( strlen($rgb) == 6 ){ - foreach(str_split($rgb, 2) as $c){ - $this->rgb[] = hexdec($c); - } - }else{ - foreach(str_split($rgb, 1) as $c){ - $this->rgb[] = hexdec($c.$c); - } - } - $this->alpha = is_numeric($a) ? $a : 1; - } - - public function compile(){ - return $this; - } - - public function luma(){ - $r = $this->rgb[0] / 255; - $g = $this->rgb[1] / 255; - $b = $this->rgb[2] / 255; - - $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4); - $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4); - $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4); - - return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->toCSS() ); - } - - public function toCSS( $doNotCompress = false ){ - $compress = Less_Parser::$options['compress'] && !$doNotCompress; - $alpha = Less_Functions::fround( $this->alpha ); - - - // - // If we have some transparency, the only way to represent it - // is via `rgba`. Otherwise, we use the hex representation, - // which has better compatibility with older browsers. - // Values are capped between `0` and `255`, rounded and zero-padded. - // - if( $alpha < 1 ){ - if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){ - return 'transparent'; - } - - $values = array(); - foreach($this->rgb as $c){ - $values[] = Less_Functions::clamp( round($c), 255); - } - $values[] = $alpha; - - $glue = ($compress ? ',' : ', '); - return "rgba(" . implode($glue, $values) . ")"; - }else{ - - $color = $this->toRGB(); - - if( $compress ){ - - // Convert color to short format - if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) { - $color = '#'.$color[1] . $color[3] . $color[5]; - } - } - - return $color; - } - } - - // - // Operations have to be done per-channel, if not, - // channels will spill onto each other. Once we have - // our result, in the form of an integer triplet, - // we create a new Color node to hold the result. - // - - /** - * @param string $op - */ - public function operate( $op, $other) { - $rgb = array(); - $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha; - for ($c = 0; $c < 3; $c++) { - $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]); - } - return new Less_Tree_Color($rgb, $alpha); - } - - public function toRGB(){ - return $this->toHex($this->rgb); - } - - public function toHSL(){ - $r = $this->rgb[0] / 255; - $g = $this->rgb[1] / 255; - $b = $this->rgb[2] / 255; - $a = $this->alpha; - - $max = max($r, $g, $b); - $min = min($r, $g, $b); - $l = ($max + $min) / 2; - $d = $max - $min; - - $h = $s = 0; - if( $max !== $min ){ - $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min); - - switch ($max) { - case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; - case $g: $h = ($b - $r) / $d + 2; break; - case $b: $h = ($r - $g) / $d + 4; break; - } - $h /= 6; - } - return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a ); - } - - //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript - public function toHSV() { - $r = $this->rgb[0] / 255; - $g = $this->rgb[1] / 255; - $b = $this->rgb[2] / 255; - $a = $this->alpha; - - $max = max($r, $g, $b); - $min = min($r, $g, $b); - - $v = $max; - - $d = $max - $min; - if ($max === 0) { - $s = 0; - } else { - $s = $d / $max; - } - - $h = 0; - if( $max !== $min ){ - switch($max){ - case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break; - case $g: $h = ($b - $r) / $d + 2; break; - case $b: $h = ($r - $g) / $d + 4; break; - } - $h /= 6; - } - return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a ); - } - - public function toARGB(){ - $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb); - return $this->toHex( $argb ); - } - - public function compare($x){ - - if( !property_exists( $x, 'rgb' ) ){ - return -1; - } - - - return ($x->rgb[0] === $this->rgb[0] && - $x->rgb[1] === $this->rgb[1] && - $x->rgb[2] === $this->rgb[2] && - $x->alpha === $this->alpha) ? 0 : -1; - } - - public function toHex( $v ){ - - $ret = '#'; - foreach($v as $c){ - $c = Less_Functions::clamp( Less_Parser::round($c), 255); - if( $c < 16 ){ - $ret .= '0'; - } - $ret .= dechex($c); - } - - return $ret; - } - - - /** - * @param string $keyword - */ - public static function fromKeyword( $keyword ){ - $keyword = strtolower($keyword); - - if( Less_Colors::hasOwnProperty($keyword) ){ - // detect named color - return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1)); - } - - if( $keyword === 'transparent' ){ - return new Less_Tree_Color( array(0, 0, 0), 0, true); - } - } - -} - - -/** - * Comment - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Comment extends Less_Tree{ - - public $value; - public $silent; - public $isReferenced; - public $currentFileInfo; - public $type = 'Comment'; - - public function __construct($value, $silent, $index = null, $currentFileInfo = null ){ - $this->value = $value; - $this->silent = !! $silent; - $this->currentFileInfo = $currentFileInfo; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - //if( $this->debugInfo ){ - //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index); - //} - $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n - } - - public function toCSS(){ - return Less_Parser::$options['compress'] ? '' : $this->value; - } - - public function isSilent(){ - $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) ); - $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value); - return $this->silent || $isReference || $isCompressed; - } - - public function compile(){ - return $this; - } - - public function markReferenced(){ - $this->isReferenced = true; - } - -} - - -/** - * Condition - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Condition extends Less_Tree{ - - public $op; - public $lvalue; - public $rvalue; - public $index; - public $negate; - public $type = 'Condition'; - - public function __construct($op, $l, $r, $i = 0, $negate = false) { - $this->op = trim($op); - $this->lvalue = $l; - $this->rvalue = $r; - $this->index = $i; - $this->negate = $negate; - } - - public function accept($visitor){ - $this->lvalue = $visitor->visitObj( $this->lvalue ); - $this->rvalue = $visitor->visitObj( $this->rvalue ); - } - - public function compile($env) { - $a = $this->lvalue->compile($env); - $b = $this->rvalue->compile($env); - - switch( $this->op ){ - case 'and': - $result = $a && $b; - break; - - case 'or': - $result = $a || $b; - break; - - default: - if( Less_Parser::is_method($a, 'compare') ){ - $result = $a->compare($b); - }elseif( Less_Parser::is_method($b, 'compare') ){ - $result = $b->compare($a); - }else{ - throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index); - } - - switch ($result) { - case -1: - $result = $this->op === '<' || $this->op === '=<' || $this->op === '<='; - break; - - case 0: - $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<='; - break; - - case 1: - $result = $this->op === '>' || $this->op === '>='; - break; - } - break; - } - - return $this->negate ? !$result : $result; - } - -} - - -/** - * DefaultFunc - * - * @package Less - * @subpackage tree - */ -class Less_Tree_DefaultFunc{ - - static $error_; - static $value_; - - public static function compile(){ - if( self::$error_ ){ - throw new Exception(self::$error_); - } - if( self::$value_ !== null ){ - return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false'); - } - } - - public static function value( $v ){ - self::$value_ = $v; - } - - public static function error( $e ){ - self::$error_ = $e; - } - - public static function reset(){ - self::$value_ = self::$error_ = null; - } -} - -/** - * DetachedRuleset - * - * @package Less - * @subpackage tree - */ -class Less_Tree_DetachedRuleset extends Less_Tree{ - - public $ruleset; - public $frames; - public $type = 'DetachedRuleset'; - - public function __construct( $ruleset, $frames = null ){ - $this->ruleset = $ruleset; - $this->frames = $frames; - } - - public function accept($visitor) { - $this->ruleset = $visitor->visitObj($this->ruleset); - } - - public function compile($env){ - if( $this->frames ){ - $frames = $this->frames; - }else{ - $frames = $env->frames; - } - return new Less_Tree_DetachedRuleset($this->ruleset, $frames); - } - - public function callEval($env) { - if( $this->frames ){ - return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) ); - } - return $this->ruleset->compile( $env ); - } -} - - - -/** - * Dimension - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Dimension extends Less_Tree{ - - public $value; - public $unit; - public $type = 'Dimension'; - - public function __construct($value, $unit = null){ - $this->value = floatval($value); - - if( $unit && ($unit instanceof Less_Tree_Unit) ){ - $this->unit = $unit; - }elseif( $unit ){ - $this->unit = new Less_Tree_Unit( array($unit) ); - }else{ - $this->unit = new Less_Tree_Unit( ); - } - } - - public function accept( $visitor ){ - $this->unit = $visitor->visitObj( $this->unit ); - } - - public function compile(){ - return $this; - } - - public function toColor() { - return new Less_Tree_Color(array($this->value, $this->value, $this->value)); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){ - throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString()); - } - - $value = Less_Functions::fround( $this->value ); - $strValue = (string)$value; - - if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){ - // would be output 1e-6 etc. - $strValue = number_format($strValue,10); - $strValue = preg_replace('/\.?0+$/','', $strValue); - } - - if( Less_Parser::$options['compress'] ){ - // Zero values doesn't need a unit - if( $value === 0 && $this->unit->isLength() ){ - $output->add( $strValue ); - return $strValue; - } - - // Float values doesn't need a leading zero - if( $value > 0 && $value < 1 && $strValue[0] === '0' ){ - $strValue = substr($strValue,1); - } - } - - $output->add( $strValue ); - $this->unit->genCSS( $output ); - } - - public function __toString(){ - return $this->toCSS(); - } - - // In an operation between two Dimensions, - // we default to the first Dimension's unit, - // so `1px + 2em` will yield `3px`. - - /** - * @param string $op - */ - public function operate( $op, $other){ - - $value = Less_Functions::operate( $op, $this->value, $other->value); - $unit = clone $this->unit; - - if( $op === '+' || $op === '-' ){ - - if( !$unit->numerator && !$unit->denominator ){ - $unit->numerator = $other->unit->numerator; - $unit->denominator = $other->unit->denominator; - }elseif( !$other->unit->numerator && !$other->unit->denominator ){ - // do nothing - }else{ - $other = $other->convertTo( $this->unit->usedUnits()); - - if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){ - throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'."); - } - - $value = Less_Functions::operate( $op, $this->value, $other->value); - } - }elseif( $op === '*' ){ - $unit->numerator = array_merge($unit->numerator, $other->unit->numerator); - $unit->denominator = array_merge($unit->denominator, $other->unit->denominator); - sort($unit->numerator); - sort($unit->denominator); - $unit->cancel(); - }elseif( $op === '/' ){ - $unit->numerator = array_merge($unit->numerator, $other->unit->denominator); - $unit->denominator = array_merge($unit->denominator, $other->unit->numerator); - sort($unit->numerator); - sort($unit->denominator); - $unit->cancel(); - } - return new Less_Tree_Dimension( $value, $unit); - } - - public function compare($other) { - if ($other instanceof Less_Tree_Dimension) { - - if( $this->unit->isEmpty() || $other->unit->isEmpty() ){ - $a = $this; - $b = $other; - } else { - $a = $this->unify(); - $b = $other->unify(); - if( $a->unit->compare($b->unit) !== 0 ){ - return -1; - } - } - $aValue = $a->value; - $bValue = $b->value; - - if ($bValue > $aValue) { - return -1; - } elseif ($bValue < $aValue) { - return 1; - } else { - return 0; - } - } else { - return -1; - } - } - - public function unify() { - return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' )); - } - - public function convertTo($conversions) { - $value = $this->value; - $unit = clone $this->unit; - - if( is_string($conversions) ){ - $derivedConversions = array(); - foreach( Less_Tree_UnitConversions::$groups as $i ){ - if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){ - $derivedConversions = array( $i => $conversions); - } - } - $conversions = $derivedConversions; - } - - - foreach($conversions as $groupName => $targetUnit){ - $group = Less_Tree_UnitConversions::${$groupName}; - - //numerator - foreach($unit->numerator as $i => $atomicUnit){ - $atomicUnit = $unit->numerator[$i]; - if( !isset($group[$atomicUnit]) ){ - continue; - } - - $value = $value * ($group[$atomicUnit] / $group[$targetUnit]); - - $unit->numerator[$i] = $targetUnit; - } - - //denominator - foreach($unit->denominator as $i => $atomicUnit){ - $atomicUnit = $unit->denominator[$i]; - if( !isset($group[$atomicUnit]) ){ - continue; - } - - $value = $value / ($group[$atomicUnit] / $group[$targetUnit]); - - $unit->denominator[$i] = $targetUnit; - } - } - - $unit->cancel(); - - return new Less_Tree_Dimension( $value, $unit); - } -} - - -/** - * Directive - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Directive extends Less_Tree{ - - public $name; - public $value; - public $rules; - public $index; - public $isReferenced; - public $currentFileInfo; - public $debugInfo; - public $type = 'Directive'; - - public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){ - $this->name = $name; - $this->value = $value; - if( $rules ){ - $this->rules = $rules; - $this->rules->allowImports = true; - } - - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - $this->debugInfo = $debugInfo; - } - - - public function accept( $visitor ){ - if( $this->rules ){ - $this->rules = $visitor->visitObj( $this->rules ); - } - if( $this->value ){ - $this->value = $visitor->visitObj( $this->value ); - } - } - - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $value = $this->value; - $rules = $this->rules; - $output->add( $this->name, $this->currentFileInfo, $this->index ); - if( $this->value ){ - $output->add(' '); - $this->value->genCSS($output); - } - if( $this->rules ){ - Less_Tree::outputRuleset( $output, array($this->rules)); - } else { - $output->add(';'); - } - } - - public function compile($env){ - - $value = $this->value; - $rules = $this->rules; - if( $value ){ - $value = $value->compile($env); - } - - if( $rules ){ - $rules = $rules->compile($env); - $rules->root = true; - } - - return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo ); - } - - - public function variable($name){ - if( $this->rules ){ - return $this->rules->variable($name); - } - } - - public function find($selector){ - if( $this->rules ){ - return $this->rules->find($selector, $this); - } - } - - //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); }, - - public function markReferenced(){ - $this->isReferenced = true; - if( $this->rules ){ - Less_Tree::ReferencedArray($this->rules->rules); - } - } - -} - - -/** - * Element - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Element extends Less_Tree{ - - public $combinator = ''; - public $value = ''; - public $index; - public $currentFileInfo; - public $type = 'Element'; - - public $value_is_object = false; - - public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){ - - $this->value = $value; - $this->value_is_object = is_object($value); - - if( $combinator ){ - $this->combinator = $combinator; - } - - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - } - - public function accept( $visitor ){ - if( $this->value_is_object ){ //object or string - $this->value = $visitor->visitObj( $this->value ); - } - } - - public function compile($env){ - - if( Less_Environment::$mixin_stack ){ - return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo ); - } - - if( $this->value_is_object ){ - $this->value = $this->value->compile($env); - } - - return $this; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->toCSS(), $this->currentFileInfo, $this->index ); - } - - public function toCSS(){ - - if( $this->value_is_object ){ - $value = $this->value->toCSS(); - }else{ - $value = $this->value; - } - - - if( $value === '' && $this->combinator && $this->combinator === '&' ){ - return ''; - } - - - return Less_Environment::$_outputMap[$this->combinator] . $value; - } - -} - - -/** - * Expression - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Expression extends Less_Tree{ - - public $value = array(); - public $parens = false; - public $parensInOp = false; - public $type = 'Expression'; - - public function __construct( $value, $parens = null ){ - $this->value = $value; - $this->parens = $parens; - } - - public function accept( $visitor ){ - $this->value = $visitor->visitArray( $this->value ); - } - - public function compile($env) { - - $doubleParen = false; - - if( $this->parens && !$this->parensInOp ){ - Less_Environment::$parensStack++; - } - - $returnValue = null; - if( $this->value ){ - - $count = count($this->value); - - if( $count > 1 ){ - - $ret = array(); - foreach($this->value as $e){ - $ret[] = $e->compile($env); - } - $returnValue = new Less_Tree_Expression($ret); - - }else{ - - if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){ - $doubleParen = true; - } - - $returnValue = $this->value[0]->compile($env); - } - - } else { - $returnValue = $this; - } - - if( $this->parens ){ - if( !$this->parensInOp ){ - Less_Environment::$parensStack--; - - }elseif( !Less_Environment::isMathOn() && !$doubleParen ){ - $returnValue = new Less_Tree_Paren($returnValue); - - } - } - return $returnValue; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $val_len = count($this->value); - for( $i = 0; $i < $val_len; $i++ ){ - $this->value[$i]->genCSS( $output ); - if( $i + 1 < $val_len ){ - $output->add( ' ' ); - } - } - } - - public function throwAwayComments() { - - if( is_array($this->value) ){ - $new_value = array(); - foreach($this->value as $v){ - if( $v instanceof Less_Tree_Comment ){ - continue; - } - $new_value[] = $v; - } - $this->value = $new_value; - } - } -} - - -/** - * Extend - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Extend extends Less_Tree{ - - public $selector; - public $option; - public $index; - public $selfSelectors = array(); - public $allowBefore; - public $allowAfter; - public $firstExtendOnThisSelectorPath; - public $type = 'Extend'; - public $ruleset; - - - public $object_id; - public $parent_ids = array(); - - /** - * @param integer $index - */ - public function __construct($selector, $option, $index){ - static $i = 0; - $this->selector = $selector; - $this->option = $option; - $this->index = $index; - - switch($option){ - case "all": - $this->allowBefore = true; - $this->allowAfter = true; - break; - default: - $this->allowBefore = false; - $this->allowAfter = false; - break; - } - - $this->object_id = $i++; - $this->parent_ids = array($this->object_id); - } - - public function accept( $visitor ){ - $this->selector = $visitor->visitObj( $this->selector ); - } - - public function compile( $env ){ - Less_Parser::$has_extends = true; - $this->selector = $this->selector->compile($env); - return $this; - //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index); - } - - public function findSelfSelectors( $selectors ){ - $selfElements = array(); - - - for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){ - $selectorElements = $selectors[$i]->elements; - // duplicate the logic in genCSS function inside the selector node. - // future TODO - move both logics into the selector joiner visitor - if( $i && $selectorElements && $selectorElements[0]->combinator === "") { - $selectorElements[0]->combinator = ' '; - } - $selfElements = array_merge( $selfElements, $selectors[$i]->elements ); - } - - $this->selfSelectors = array(new Less_Tree_Selector($selfElements)); - } - -} - -/** - * CSS @import node - * - * The general strategy here is that we don't want to wait - * for the parsing to be completed, before we start importing - * the file. That's because in the context of a browser, - * most of the time will be spent waiting for the server to respond. - * - * On creation, we push the import path to our import queue, though - * `import,push`, we also pass it a callback, which it'll call once - * the file has been fetched, and parsed. - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Import extends Less_Tree{ - - public $options; - public $index; - public $path; - public $features; - public $currentFileInfo; - public $css; - public $skip; - public $root; - public $type = 'Import'; - - public function __construct($path, $features, $options, $index, $currentFileInfo = null ){ - $this->options = $options; - $this->index = $index; - $this->path = $path; - $this->features = $features; - $this->currentFileInfo = $currentFileInfo; - - if( is_array($options) ){ - $this->options += array('inline'=>false); - - if( isset($this->options['less']) || $this->options['inline'] ){ - $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline']; - } else { - $pathValue = $this->getPath(); - if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){ - $this->css = true; - } - } - } - } - -// -// The actual import node doesn't return anything, when converted to CSS. -// The reason is that it's used at the evaluation stage, so that the rules -// it imports can be treated like any other rules. -// -// In `eval`, we make sure all Import nodes get evaluated, recursively, so -// we end up with a flat structure, which can easily be imported in the parent -// ruleset. -// - - public function accept($visitor){ - - if( $this->features ){ - $this->features = $visitor->visitObj($this->features); - } - $this->path = $visitor->visitObj($this->path); - - if( !$this->options['inline'] && $this->root ){ - $this->root = $visitor->visit($this->root); - } - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - if( $this->css ){ - - $output->add( '@import ', $this->currentFileInfo, $this->index ); - - $this->path->genCSS( $output ); - if( $this->features ){ - $output->add( ' ' ); - $this->features->genCSS( $output ); - } - $output->add( ';' ); - } - } - - public function toCSS(){ - $features = $this->features ? ' ' . $this->features->toCSS() : ''; - - if ($this->css) { - return "@import " . $this->path->toCSS() . $features . ";\n"; - } else { - return ""; - } - } - - /** - * @return string - */ - public function getPath(){ - if ($this->path instanceof Less_Tree_Quoted) { - $path = $this->path->value; - $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less'; - } else if ($this->path instanceof Less_Tree_URL) { - $path = $this->path->value->value; - }else{ - return null; - } - - //remove query string and fragment - return preg_replace('/[\?#][^\?]*$/','',$path); - } - - public function compileForImport( $env ){ - return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo); - } - - public function compilePath($env) { - $path = $this->path->compile($env); - $rootpath = ''; - if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){ - $rootpath = $this->currentFileInfo['rootpath']; - } - - - if( !($path instanceof Less_Tree_URL) ){ - if( $rootpath ){ - $pathValue = $path->value; - // Add the base path if the import is relative - if( $pathValue && Less_Environment::isPathRelative($pathValue) ){ - $path->value = $this->currentFileInfo['uri_root'].$pathValue; - } - } - $path->value = Less_Environment::normalizePath($path->value); - } - - - - return $path; - } - - public function compile( $env ){ - - $evald = $this->compileForImport($env); - - //get path & uri - $path_and_uri = null; - if( is_callable(Less_Parser::$options['import_callback']) ){ - $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald); - } - - if( !$path_and_uri ){ - $path_and_uri = $evald->PathAndUri(); - } - - if( $path_and_uri ){ - list($full_path, $uri) = $path_and_uri; - }else{ - $full_path = $uri = $evald->getPath(); - } - - - //import once - if( $evald->skip( $full_path, $env) ){ - return array(); - } - - if( $this->options['inline'] ){ - //todo needs to reference css file not import - //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true ); - - Less_Parser::AddParsedFile($full_path); - $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true ); - - if( $this->features ){ - return new Less_Tree_Media( array($contents), $this->features->value ); - } - - return array( $contents ); - } - - // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo)) - if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) { - return array(); - } - - - // css ? - if( $evald->css ){ - $features = ( $evald->features ? $evald->features->compile($env) : null ); - return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index); - } - - - return $this->ParseImport( $full_path, $uri, $env ); - } - - - /** - * Using the import directories, get the full absolute path and uri of the import - * - * @param Less_Tree_Import $evald - */ - public function PathAndUri(){ - - $evald_path = $this->getPath(); - - if( $evald_path ){ - - $import_dirs = array(); - - if( Less_Environment::isPathRelative($evald_path) ){ - //if the path is relative, the file should be in the current directory - $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root']; - - }else{ - //otherwise, the file should be relative to the server root - $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri']; - - //if the user supplied entryPath isn't the actual root - $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = ''; - - } - - // always look in user supplied import directories - $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] ); - - - foreach( $import_dirs as $rootpath => $rooturi){ - if( is_callable($rooturi) ){ - list($path, $uri) = call_user_func($rooturi, $evald_path); - if( is_string($path) ){ - $full_path = $path; - return array( $full_path, $uri ); - } - }elseif( !empty($rootpath) ){ - - - if( $rooturi ){ - if( strpos($evald_path,$rooturi) === 0 ){ - $evald_path = substr( $evald_path, strlen($rooturi) ); - } - } - - $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\'); - - if( file_exists($path) ){ - $full_path = Less_Environment::normalizePath($path); - $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path)); - return array( $full_path, $uri ); - } elseif( file_exists($path.'.less') ){ - $full_path = Less_Environment::normalizePath($path.'.less'); - $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less')); - return array( $full_path, $uri ); - } - } - } - } - } - - - /** - * Parse the import url and return the rules - * - * @return Less_Tree_Media|array - */ - public function ParseImport( $full_path, $uri, $env ){ - - $import_env = clone $env; - if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){ - $import_env->currentFileInfo['reference'] = true; - } - - if( (isset($this->options['multiple']) && $this->options['multiple']) ){ - $import_env->importMultiple = true; - } - - $parser = new Less_Parser($import_env); - $root = $parser->parseFile($full_path, $uri, true); - - - $ruleset = new Less_Tree_Ruleset(array(), $root->rules ); - $ruleset->evalImports($import_env); - - return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules; - } - - - /** - * Should the import be skipped? - * - * @return boolean|null - */ - private function Skip($path, $env){ - - $path = Less_Parser::winPath(realpath($path)); - - if( $path && Less_Parser::FileParsed($path) ){ - - if( isset($this->currentFileInfo['reference']) ){ - return true; - } - - return !isset($this->options['multiple']) && !$env->importMultiple; - } - - } -} - - -/** - * Javascript - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Javascript extends Less_Tree{ - - public $type = 'Javascript'; - public $escaped; - public $expression; - public $index; - - /** - * @param boolean $index - * @param boolean $escaped - */ - public function __construct($string, $index, $escaped){ - $this->escaped = $escaped; - $this->expression = $string; - $this->index = $index; - } - - public function compile(){ - return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */'); - } - -} - - -/** - * Keyword - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Keyword extends Less_Tree{ - - public $value; - public $type = 'Keyword'; - - /** - * @param string $value - */ - public function __construct($value){ - $this->value = $value; - } - - public function compile(){ - return $this; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - if( $this->value === '%') { - throw new Less_Exception_Compiler("Invalid % without number"); - } - - $output->add( $this->value ); - } - - public function compare($other) { - if ($other instanceof Less_Tree_Keyword) { - return $other->value === $this->value ? 0 : 1; - } else { - return -1; - } - } -} - - -/** - * Media - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Media extends Less_Tree{ - - public $features; - public $rules; - public $index; - public $currentFileInfo; - public $isReferenced; - public $type = 'Media'; - - public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){ - - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - - $selectors = $this->emptySelectors(); - - $this->features = new Less_Tree_Value($features); - - $this->rules = array(new Less_Tree_Ruleset($selectors, $value)); - $this->rules[0]->allowImports = true; - } - - public function accept( $visitor ){ - $this->features = $visitor->visitObj($this->features); - $this->rules = $visitor->visitArray($this->rules); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - $output->add( '@media ', $this->currentFileInfo, $this->index ); - $this->features->genCSS( $output ); - Less_Tree::outputRuleset( $output, $this->rules); - - } - - public function compile($env) { - - $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo ); - - $strictMathBypass = false; - if( Less_Parser::$options['strictMath'] === false) { - $strictMathBypass = true; - Less_Parser::$options['strictMath'] = true; - } - - $media->features = $this->features->compile($env); - - if( $strictMathBypass ){ - Less_Parser::$options['strictMath'] = false; - } - - $env->mediaPath[] = $media; - $env->mediaBlocks[] = $media; - - array_unshift($env->frames, $this->rules[0]); - $media->rules = array($this->rules[0]->compile($env)); - array_shift($env->frames); - - array_pop($env->mediaPath); - - return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env); - } - - public function variable($name) { - return $this->rules[0]->variable($name); - } - - public function find($selector) { - return $this->rules[0]->find($selector, $this); - } - - public function emptySelectors(){ - $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo ); - $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) ); - $sels[0]->mediaEmpty = true; - return $sels; - } - - public function markReferenced(){ - $this->rules[0]->markReferenced(); - $this->isReferenced = true; - Less_Tree::ReferencedArray($this->rules[0]->rules); - } - - // evaltop - public function compileTop($env) { - $result = $this; - - if (count($env->mediaBlocks) > 1) { - $selectors = $this->emptySelectors(); - $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks); - $result->multiMedia = true; - } - - $env->mediaBlocks = array(); - $env->mediaPath = array(); - - return $result; - } - - public function compileNested($env) { - $path = array_merge($env->mediaPath, array($this)); - - // Extract the media-query conditions separated with `,` (OR). - foreach ($path as $key => $p) { - $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features; - $path[$key] = is_array($value) ? $value : array($value); - } - - // Trace all permutations to generate the resulting media-query. - // - // (a, b and c) with nested (d, e) -> - // a and d - // a and e - // b and c and d - // b and c and e - - $permuted = $this->permute($path); - $expressions = array(); - foreach($permuted as $path){ - - for( $i=0, $len=count($path); $i < $len; $i++){ - $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]); - } - - for( $i = count($path) - 1; $i > 0; $i-- ){ - array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and'))); - } - - $expressions[] = new Less_Tree_Expression($path); - } - $this->features = new Less_Tree_Value($expressions); - - - - // Fake a tree-node that doesn't output anything. - return new Less_Tree_Ruleset(array(), array()); - } - - public function permute($arr) { - if (!$arr) - return array(); - - if (count($arr) == 1) - return $arr[0]; - - $result = array(); - $rest = $this->permute(array_slice($arr, 1)); - foreach ($rest as $r) { - foreach ($arr[0] as $a) { - $result[] = array_merge( - is_array($a) ? $a : array($a), - is_array($r) ? $r : array($r) - ); - } - } - - return $result; - } - - public function bubbleSelectors($selectors) { - - if( !$selectors) return; - - $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0]))); - } - -} - - -/** - * A simple css name-value pair - * ex: width:100px; - * - * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule) - * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000; - * - * @package Less - * @subpackage tree - */ -class Less_Tree_NameValue extends Less_Tree{ - - public $name; - public $value; - public $index; - public $currentFileInfo; - public $type = 'NameValue'; - public $important = ''; - - public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){ - $this->name = $name; - $this->value = $value; - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - } - - public function genCSS( $output ){ - - $output->add( - $this->name - . Less_Environment::$_outputMap[': '] - . $this->value - . $this->important - . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";") - , $this->currentFileInfo, $this->index); - } - - public function compile ($env){ - return $this; - } - - public function makeImportant(){ - $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo); - $new->important = ' !important'; - return $new; - } - - -} - - -/** - * Negative - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Negative extends Less_Tree{ - - public $value; - public $type = 'Negative'; - - public function __construct($node){ - $this->value = $node; - } - - //function accept($visitor) { - // $this->value = $visitor->visit($this->value); - //} - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( '-' ); - $this->value->genCSS( $output ); - } - - public function compile($env) { - if( Less_Environment::isMathOn() ){ - $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) ); - return $ret->compile($env); - } - return new Less_Tree_Negative( $this->value->compile($env) ); - } -} - -/** - * Operation - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Operation extends Less_Tree{ - - public $op; - public $operands; - public $isSpaced; - public $type = 'Operation'; - - /** - * @param string $op - */ - public function __construct($op, $operands, $isSpaced = false){ - $this->op = trim($op); - $this->operands = $operands; - $this->isSpaced = $isSpaced; - } - - public function accept($visitor) { - $this->operands = $visitor->visitArray($this->operands); - } - - public function compile($env){ - $a = $this->operands[0]->compile($env); - $b = $this->operands[1]->compile($env); - - - if( Less_Environment::isMathOn() ){ - - if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){ - $a = $a->toColor(); - - }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){ - $b = $b->toColor(); - - } - - if( !method_exists($a,'operate') ){ - throw new Less_Exception_Compiler("Operation on an invalid type"); - } - - return $a->operate( $this->op, $b); - } - - return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced ); - } - - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $this->operands[0]->genCSS( $output ); - if( $this->isSpaced ){ - $output->add( " " ); - } - $output->add( $this->op ); - if( $this->isSpaced ){ - $output->add( ' ' ); - } - $this->operands[1]->genCSS( $output ); - } - -} - - -/** - * Paren - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Paren extends Less_Tree{ - - public $value; - public $type = 'Paren'; - - public function __construct($value) { - $this->value = $value; - } - - public function accept($visitor){ - $this->value = $visitor->visitObj($this->value); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( '(' ); - $this->value->genCSS( $output ); - $output->add( ')' ); - } - - public function compile($env) { - return new Less_Tree_Paren($this->value->compile($env)); - } - -} - - -/** - * Quoted - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Quoted extends Less_Tree{ - public $escaped; - public $value; - public $quote; - public $index; - public $currentFileInfo; - public $type = 'Quoted'; - - /** - * @param string $str - */ - public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){ - $this->escaped = $escaped; - $this->value = $content; - if( $str ){ - $this->quote = $str[0]; - } - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - if( !$this->escaped ){ - $output->add( $this->quote, $this->currentFileInfo, $this->index ); - } - $output->add( $this->value ); - if( !$this->escaped ){ - $output->add( $this->quote ); - } - } - - public function compile($env){ - - $value = $this->value; - if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){ - foreach($matches as $i => $match){ - $js = new Less_Tree_JavaScript($matches[1], $this->index, true); - $js = $js->compile()->value; - $value = str_replace($matches[0][$i], $js, $value); - } - } - - if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){ - foreach($matches[1] as $i => $match){ - $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo ); - $v = $v->compile($env); - $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS(); - $value = str_replace($matches[0][$i], $v, $value); - } - } - - return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo); - } - - public function compare($x) { - - if( !Less_Parser::is_method($x, 'toCSS') ){ - return -1; - } - - $left = $this->toCSS(); - $right = $x->toCSS(); - - if ($left === $right) { - return 0; - } - - return $left < $right ? -1 : 1; - } -} - - -/** - * Rule - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Rule extends Less_Tree{ - - public $name; - public $value; - public $important; - public $merge; - public $index; - public $inline; - public $variable; - public $currentFileInfo; - public $type = 'Rule'; - - /** - * @param string $important - */ - public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false){ - $this->name = $name; - $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value)); - $this->important = $important ? ' ' . trim($important) : ''; - $this->merge = $merge; - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - $this->inline = $inline; - $this->variable = ( is_string($name) && $name[0] === '@'); - } - - public function accept($visitor) { - $this->value = $visitor->visitObj( $this->value ); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index); - try{ - $this->value->genCSS( $output); - - }catch( Less_Exception_Parser $e ){ - $e->index = $this->index; - $e->currentFile = $this->currentFileInfo; - throw $e; - } - $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index); - } - - public function compile ($env){ - - $name = $this->name; - if( is_array($name) ){ - // expand 'primitive' name directly to get - // things faster (~10% for benchmark.less): - if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){ - $name = $name[0]->value; - }else{ - $name = $this->CompileName($env,$name); - } - } - - $strictMathBypass = Less_Parser::$options['strictMath']; - if( $name === "font" && !Less_Parser::$options['strictMath'] ){ - Less_Parser::$options['strictMath'] = true; - } - - try { - $evaldValue = $this->value->compile($env); - - if( !$this->variable && $evaldValue->type === "DetachedRuleset") { - throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo); - } - - if( Less_Environment::$mixin_stack ){ - $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline); - }else{ - $this->name = $name; - $this->value = $evaldValue; - $return = $this; - } - - }catch( Less_Exception_Parser $e ){ - if( !is_numeric($e->index) ){ - $e->index = $this->index; - $e->currentFile = $this->currentFileInfo; - } - throw $e; - } - - Less_Parser::$options['strictMath'] = $strictMathBypass; - - return $return; - } - - - public function CompileName( $env, $name ){ - $output = new Less_Output(); - foreach($name as $n){ - $n->compile($env)->genCSS($output); - } - return $output->toString(); - } - - public function makeImportant(){ - return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline); - } - -} - - -/** - * Ruleset - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Ruleset extends Less_Tree{ - - protected $lookups; - public $_variables; - public $_rulesets; - - public $strictImports; - - public $selectors; - public $rules; - public $root; - public $allowImports; - public $paths; - public $firstRoot; - public $type = 'Ruleset'; - public $multiMedia; - public $allExtends; - - public $ruleset_id; - public $originalRuleset; - - public $first_oelements; - - public function SetRulesetIndex(){ - $this->ruleset_id = Less_Parser::$next_id++; - $this->originalRuleset = $this->ruleset_id; - - if( $this->selectors ){ - foreach($this->selectors as $sel){ - if( $sel->_oelements ){ - $this->first_oelements[$sel->_oelements[0]] = true; - } - } - } - } - - public function __construct($selectors, $rules, $strictImports = null){ - $this->selectors = $selectors; - $this->rules = $rules; - $this->lookups = array(); - $this->strictImports = $strictImports; - $this->SetRulesetIndex(); - } - - public function accept( $visitor ){ - if( $this->paths ){ - $paths_len = count($this->paths); - for($i = 0,$paths_len; $i < $paths_len; $i++ ){ - $this->paths[$i] = $visitor->visitArray($this->paths[$i]); - } - }elseif( $this->selectors ){ - $this->selectors = $visitor->visitArray($this->selectors); - } - - if( $this->rules ){ - $this->rules = $visitor->visitArray($this->rules); - } - } - - public function compile($env){ - - $ruleset = $this->PrepareRuleset($env); - - - // Store the frames around mixin definitions, - // so they can be evaluated like closures when the time comes. - $rsRuleCnt = count($ruleset->rules); - for( $i = 0; $i < $rsRuleCnt; $i++ ){ - if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){ - $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); - } - } - - $mediaBlockCount = 0; - if( $env instanceof Less_Environment ){ - $mediaBlockCount = count($env->mediaBlocks); - } - - // Evaluate mixin calls. - $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt ); - - - // Evaluate everything else - for( $i=0; $i<$rsRuleCnt; $i++ ){ - if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){ - $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env); - } - } - - // Evaluate everything else - for( $i=0; $i<$rsRuleCnt; $i++ ){ - $rule = $ruleset->rules[$i]; - - // for rulesets, check if it is a css guard and can be removed - if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){ - - // check if it can be folded in (e.g. & where) - if( $rule->selectors[0]->isJustParentSelector() ){ - array_splice($ruleset->rules,$i--,1); - $rsRuleCnt--; - - for($j = 0; $j < count($rule->rules); $j++ ){ - $subRule = $rule->rules[$j]; - if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){ - array_splice($ruleset->rules, ++$i, 0, array($subRule)); - $rsRuleCnt++; - } - } - - } - } - } - - - // Pop the stack - $env->shiftFrame(); - - if ($mediaBlockCount) { - $len = count($env->mediaBlocks); - for($i = $mediaBlockCount; $i < $len; $i++ ){ - $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors); - } - } - - return $ruleset; - } - - /** - * Compile Less_Tree_Mixin_Call objects - * - * @param Less_Tree_Ruleset $ruleset - * @param integer $rsRuleCnt - */ - private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){ - for($i=0; $i < $rsRuleCnt; $i++){ - $rule = $ruleset->rules[$i]; - - if( $rule instanceof Less_Tree_Mixin_Call ){ - $rule = $rule->compile($env); - - $temp = array(); - foreach($rule as $r){ - if( ($r instanceof Less_Tree_Rule) && $r->variable ){ - // do not pollute the scope if the variable is - // already there. consider returning false here - // but we need a way to "return" variable from mixins - if( !$ruleset->variable($r->name) ){ - $temp[] = $r; - } - }else{ - $temp[] = $r; - } - } - $temp_count = count($temp)-1; - array_splice($ruleset->rules, $i, 1, $temp); - $rsRuleCnt += $temp_count; - $i += $temp_count; - $ruleset->resetCache(); - - }elseif( $rule instanceof Less_Tree_RulesetCall ){ - - $rule = $rule->compile($env); - $rules = array(); - foreach($rule->rules as $r){ - if( ($r instanceof Less_Tree_Rule) && $r->variable ){ - continue; - } - $rules[] = $r; - } - - array_splice($ruleset->rules, $i, 1, $rules); - $temp_count = count($rules); - $rsRuleCnt += $temp_count - 1; - $i += $temp_count-1; - $ruleset->resetCache(); - } - - } - } - - - /** - * Compile the selectors and create a new ruleset object for the compile() method - * - */ - private function PrepareRuleset($env){ - - $hasOnePassingSelector = false; - $selectors = array(); - if( $this->selectors ){ - Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,"); - - foreach($this->selectors as $s){ - $selector = $s->compile($env); - $selectors[] = $selector; - if( $selector->evaldCondition ){ - $hasOnePassingSelector = true; - } - } - - Less_Tree_DefaultFunc::reset(); - } else { - $hasOnePassingSelector = true; - } - - if( $this->rules && $hasOnePassingSelector ){ - $rules = $this->rules; - }else{ - $rules = array(); - } - - $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports); - - $ruleset->originalRuleset = $this->ruleset_id; - - $ruleset->root = $this->root; - $ruleset->firstRoot = $this->firstRoot; - $ruleset->allowImports = $this->allowImports; - - - // push the current ruleset to the frames stack - $env->unshiftFrame($ruleset); - - - // Evaluate imports - if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){ - $ruleset->evalImports($env); - } - - return $ruleset; - } - - function evalImports($env) { - - $rules_len = count($this->rules); - for($i=0; $i < $rules_len; $i++){ - $rule = $this->rules[$i]; - - if( $rule instanceof Less_Tree_Import ){ - $rules = $rule->compile($env); - if( is_array($rules) ){ - array_splice($this->rules, $i, 1, $rules); - $temp_count = count($rules)-1; - $i += $temp_count; - $rules_len += $temp_count; - }else{ - array_splice($this->rules, $i, 1, array($rules)); - } - - $this->resetCache(); - } - } - } - - function makeImportant(){ - - $important_rules = array(); - foreach($this->rules as $rule){ - if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){ - $important_rules[] = $rule->makeImportant(); - }else{ - $important_rules[] = $rule; - } - } - - return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports ); - } - - public function matchArgs($args){ - return !$args; - } - - // lets you call a css selector with a guard - public function matchCondition( $args, $env ){ - $lastSelector = end($this->selectors); - - if( !$lastSelector->evaldCondition ){ - return false; - } - if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){ - return false; - } - return true; - } - - function resetCache(){ - $this->_rulesets = null; - $this->_variables = null; - $this->lookups = array(); - } - - public function variables(){ - $this->_variables = array(); - foreach( $this->rules as $r){ - if ($r instanceof Less_Tree_Rule && $r->variable === true) { - $this->_variables[$r->name] = $r; - } - } - } - - public function variable($name){ - - if( is_null($this->_variables) ){ - $this->variables(); - } - return isset($this->_variables[$name]) ? $this->_variables[$name] : null; - } - - public function find( $selector, $self = null ){ - - $key = implode(' ',$selector->_oelements); - - if( !isset($this->lookups[$key]) ){ - - if( !$self ){ - $self = $this->ruleset_id; - } - - $this->lookups[$key] = array(); - - $first_oelement = $selector->_oelements[0]; - - foreach($this->rules as $rule){ - if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){ - - if( isset($rule->first_oelements[$first_oelement]) ){ - - foreach( $rule->selectors as $ruleSelector ){ - $match = $selector->match($ruleSelector); - if( $match ){ - if( $selector->elements_len > $match ){ - $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self)); - } else { - $this->lookups[$key][] = $rule; - } - break; - } - } - } - } - } - } - - return $this->lookups[$key]; - } - - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - if( !$this->root ){ - Less_Environment::$tabLevel++; - } - - $tabRuleStr = $tabSetStr = ''; - if( !Less_Parser::$options['compress'] ){ - if( Less_Environment::$tabLevel ){ - $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel ); - $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 ); - }else{ - $tabSetStr = $tabRuleStr = "\n"; - } - } - - - $ruleNodes = array(); - $rulesetNodes = array(); - foreach($this->rules as $rule){ - - $class = get_class($rule); - if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){ - $rulesetNodes[] = $rule; - }else{ - $ruleNodes[] = $rule; - } - } - - // If this is the root node, we don't render - // a selector, or {}. - if( !$this->root ){ - - /* - debugInfo = tree.debugInfo(env, this, tabSetStr); - - if (debugInfo) { - output.add(debugInfo); - output.add(tabSetStr); - } - */ - - $paths_len = count($this->paths); - for( $i = 0; $i < $paths_len; $i++ ){ - $path = $this->paths[$i]; - $firstSelector = true; - - foreach($path as $p){ - $p->genCSS( $output, $firstSelector ); - $firstSelector = false; - } - - if( $i + 1 < $paths_len ){ - $output->add( ',' . $tabSetStr ); - } - } - - $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr ); - } - - // Compile rules and rulesets - $ruleNodes_len = count($ruleNodes); - $rulesetNodes_len = count($rulesetNodes); - for( $i = 0; $i < $ruleNodes_len; $i++ ){ - $rule = $ruleNodes[$i]; - - // @page{ directive ends up with root elements inside it, a mix of rules and rulesets - // In this instance we do not know whether it is the last property - if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){ - Less_Environment::$lastRule = true; - } - - $rule->genCSS( $output ); - - if( !Less_Environment::$lastRule ){ - $output->add( $tabRuleStr ); - }else{ - Less_Environment::$lastRule = false; - } - } - - if( !$this->root ){ - $output->add( $tabSetStr . '}' ); - Less_Environment::$tabLevel--; - } - - $firstRuleset = true; - $space = ($this->root ? $tabRuleStr : $tabSetStr); - for( $i = 0; $i < $rulesetNodes_len; $i++ ){ - - if( $ruleNodes_len && $firstRuleset ){ - $output->add( $space ); - }elseif( !$firstRuleset ){ - $output->add( $space ); - } - $firstRuleset = false; - $rulesetNodes[$i]->genCSS( $output); - } - - if( !Less_Parser::$options['compress'] && $this->firstRoot ){ - $output->add( "\n" ); - } - - } - - - function markReferenced(){ - if( !$this->selectors ){ - return; - } - foreach($this->selectors as $selector){ - $selector->markReferenced(); - } - } - - public function joinSelectors( $context, $selectors ){ - $paths = array(); - if( is_array($selectors) ){ - foreach($selectors as $selector) { - $this->joinSelector( $paths, $context, $selector); - } - } - return $paths; - } - - public function joinSelector( &$paths, $context, $selector){ - - $hasParentSelector = false; - - foreach($selector->elements as $el) { - if( $el->value === '&') { - $hasParentSelector = true; - } - } - - if( !$hasParentSelector ){ - if( $context ){ - foreach($context as $context_el){ - $paths[] = array_merge($context_el, array($selector) ); - } - }else { - $paths[] = array($selector); - } - return; - } - - - // The paths are [[Selector]] - // The first list is a list of comma seperated selectors - // The inner list is a list of inheritance seperated selectors - // e.g. - // .a, .b { - // .c { - // } - // } - // == [[.a] [.c]] [[.b] [.c]] - // - - // the elements from the current selector so far - $currentElements = array(); - // the current list of new selectors to add to the path. - // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors - // by the parents - $newSelectors = array(array()); - - - foreach( $selector->elements as $el){ - - // non parent reference elements just get added - if( $el->value !== '&' ){ - $currentElements[] = $el; - } else { - // the new list of selectors to add - $selectorsMultiplied = array(); - - // merge the current list of non parent selector elements - // on to the current list of selectors to add - if( $currentElements ){ - $this->mergeElementsOnToSelectors( $currentElements, $newSelectors); - } - - // loop through our current selectors - foreach($newSelectors as $sel){ - - // if we don't have any parent paths, the & might be in a mixin so that it can be used - // whether there are parents or not - if( !$context ){ - // the combinator used on el should now be applied to the next element instead so that - // it is not lost - if( $sel ){ - $sel[0]->elements = array_slice($sel[0]->elements,0); - $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo ); - } - $selectorsMultiplied[] = $sel; - }else { - - // and the parent selectors - foreach($context as $parentSel){ - // We need to put the current selectors - // then join the last selector's elements on to the parents selectors - - // our new selector path - $newSelectorPath = array(); - // selectors from the parent after the join - $afterParentJoin = array(); - $newJoinedSelectorEmpty = true; - - //construct the joined selector - if & is the first thing this will be empty, - // if not newJoinedSelector will be the last set of elements in the selector - if( $sel ){ - $newSelectorPath = $sel; - $lastSelector = array_pop($newSelectorPath); - $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) ); - $newJoinedSelectorEmpty = false; - } - else { - $newJoinedSelector = $selector->createDerived(array()); - } - - //put together the parent selectors after the join - if ( count($parentSel) > 1) { - $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) ); - } - - if ( $parentSel ){ - $newJoinedSelectorEmpty = false; - - // join the elements so far with the first part of the parent - $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo); - - $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) ); - } - - if (!$newJoinedSelectorEmpty) { - // now add the joined selector - $newSelectorPath[] = $newJoinedSelector; - } - - // and the rest of the parent - $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin); - - // add that to our new set of selectors - $selectorsMultiplied[] = $newSelectorPath; - } - } - } - - // our new selectors has been multiplied, so reset the state - $newSelectors = $selectorsMultiplied; - $currentElements = array(); - } - } - - // if we have any elements left over (e.g. .a& .b == .b) - // add them on to all the current selectors - if( $currentElements ){ - $this->mergeElementsOnToSelectors($currentElements, $newSelectors); - } - foreach( $newSelectors as $new_sel){ - if( $new_sel ){ - $paths[] = $new_sel; - } - } - } - - function mergeElementsOnToSelectors( $elements, &$selectors){ - - if( !$selectors ){ - $selectors[] = array( new Less_Tree_Selector($elements) ); - return; - } - - - foreach( $selectors as &$sel){ - - // if the previous thing in sel is a parent this needs to join on to it - if( $sel ){ - $last = count($sel)-1; - $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) ); - }else{ - $sel[] = new Less_Tree_Selector( $elements ); - } - } - } -} - - -/** - * RulesetCall - * - * @package Less - * @subpackage tree - */ -class Less_Tree_RulesetCall extends Less_Tree{ - - public $variable; - public $type = "RulesetCall"; - - public function __construct($variable){ - $this->variable = $variable; - } - - public function accept($visitor) {} - - public function compile( $env ){ - $variable = new Less_Tree_Variable($this->variable); - $detachedRuleset = $variable->compile($env); - return $detachedRuleset->callEval($env); - } -} - - - -/** - * Selector - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Selector extends Less_Tree{ - - public $elements; - public $condition; - public $extendList = array(); - public $_css; - public $index; - public $evaldCondition = false; - public $type = 'Selector'; - public $currentFileInfo = array(); - public $isReferenced; - public $mediaEmpty; - - public $elements_len = 0; - - public $_oelements; - public $_oelements_len; - public $cacheable = true; - - /** - * @param boolean $isReferenced - */ - public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){ - - $this->elements = $elements; - $this->elements_len = count($elements); - $this->extendList = $extendList; - $this->condition = $condition; - if( $currentFileInfo ){ - $this->currentFileInfo = $currentFileInfo; - } - $this->isReferenced = $isReferenced; - if( !$condition ){ - $this->evaldCondition = true; - } - - $this->CacheElements(); - } - - public function accept($visitor) { - $this->elements = $visitor->visitArray($this->elements); - $this->extendList = $visitor->visitArray($this->extendList); - if( $this->condition ){ - $this->condition = $visitor->visitObj($this->condition); - } - - if( $visitor instanceof Less_Visitor_extendFinder ){ - $this->CacheElements(); - } - } - - public function createDerived( $elements, $extendList = null, $evaldCondition = null ){ - $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced); - $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition; - return $newSelector; - } - - - public function match( $other ){ - - if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){ - return 0; - } - - for( $i = 0; $i < $other->_oelements_len; $i++ ){ - if( $this->elements[$i]->value !== $other->_oelements[$i]) { - return 0; - } - } - - return $other->_oelements_len; // return number of matched elements - } - - - public function CacheElements(){ - - $this->_oelements = array(); - $css = ''; - - foreach($this->elements as $v){ - - $css .= $v->combinator; - if( !$v->value_is_object ){ - $css .= $v->value; - continue; - } - - if( !property_exists($v->value,'value') || !is_string($v->value->value) ){ - $this->cacheable = false; - return; - } - $css .= $v->value->value; - } - - $this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches); - if( $this->_oelements_len ){ - $this->_oelements = $matches[0]; - - if( $this->_oelements[0] === '&' ){ - array_shift($this->_oelements); - $this->_oelements_len--; - } - } - } - - public function isJustParentSelector(){ - return !$this->mediaEmpty && - count($this->elements) === 1 && - $this->elements[0]->value === '&' && - ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === ''); - } - - public function compile($env) { - - $elements = array(); - foreach($this->elements as $el){ - $elements[] = $el->compile($env); - } - - $extendList = array(); - foreach($this->extendList as $el){ - $extendList[] = $el->compile($el); - } - - $evaldCondition = false; - if( $this->condition ){ - $evaldCondition = $this->condition->compile($env); - } - - return $this->createDerived( $elements, $extendList, $evaldCondition ); - } - - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output, $firstSelector = true ){ - - if( !$firstSelector && $this->elements[0]->combinator === "" ){ - $output->add(' ', $this->currentFileInfo, $this->index); - } - - foreach($this->elements as $element){ - $element->genCSS( $output ); - } - } - - public function markReferenced(){ - $this->isReferenced = true; - } - - public function getIsReferenced(){ - return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced; - } - - public function getIsOutput(){ - return $this->evaldCondition; - } - -} - - -/** - * UnicodeDescriptor - * - * @package Less - * @subpackage tree - */ -class Less_Tree_UnicodeDescriptor extends Less_Tree{ - - public $value; - public $type = 'UnicodeDescriptor'; - - public function __construct($value){ - $this->value = $value; - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( $this->value ); - } - - public function compile(){ - return $this; - } -} - - - -/** - * Unit - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Unit extends Less_Tree{ - - var $numerator = array(); - var $denominator = array(); - public $backupUnit; - public $type = 'Unit'; - - public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){ - $this->numerator = $numerator; - $this->denominator = $denominator; - $this->backupUnit = $backupUnit; - } - - public function __clone(){ - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - - if( $this->numerator ){ - $output->add( $this->numerator[0] ); - }elseif( $this->denominator ){ - $output->add( $this->denominator[0] ); - }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){ - $output->add( $this->backupUnit ); - return ; - } - } - - public function toString(){ - $returnStr = implode('*',$this->numerator); - foreach($this->denominator as $d){ - $returnStr .= '/'.$d; - } - return $returnStr; - } - - public function __toString(){ - return $this->toString(); - } - - - /** - * @param Less_Tree_Unit $other - */ - public function compare($other) { - return $this->is( $other->toString() ) ? 0 : -1; - } - - public function is($unitString){ - return $this->toString() === $unitString; - } - - public function isLength(){ - $css = $this->toCSS(); - return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css); - } - - public function isAngle() { - return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] ); - } - - public function isEmpty(){ - return !$this->numerator && !$this->denominator; - } - - public function isSingular() { - return count($this->numerator) <= 1 && !$this->denominator; - } - - - public function usedUnits(){ - $result = array(); - - foreach(Less_Tree_UnitConversions::$groups as $groupName){ - $group = Less_Tree_UnitConversions::${$groupName}; - - foreach($this->numerator as $atomicUnit){ - if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){ - $result[$groupName] = $atomicUnit; - } - } - - foreach($this->denominator as $atomicUnit){ - if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){ - $result[$groupName] = $atomicUnit; - } - } - } - - return $result; - } - - public function cancel(){ - $counter = array(); - $backup = null; - - foreach($this->numerator as $atomicUnit){ - if( !$backup ){ - $backup = $atomicUnit; - } - $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1; - } - - foreach($this->denominator as $atomicUnit){ - if( !$backup ){ - $backup = $atomicUnit; - } - $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1; - } - - $this->numerator = array(); - $this->denominator = array(); - - foreach($counter as $atomicUnit => $count){ - if( $count > 0 ){ - for( $i = 0; $i < $count; $i++ ){ - $this->numerator[] = $atomicUnit; - } - }elseif( $count < 0 ){ - for( $i = 0; $i < -$count; $i++ ){ - $this->denominator[] = $atomicUnit; - } - } - } - - if( !$this->numerator && !$this->denominator && $backup ){ - $this->backupUnit = $backup; - } - - sort($this->numerator); - sort($this->denominator); - } - - -} - - - -/** - * UnitConversions - * - * @package Less - * @subpackage tree - */ -class Less_Tree_UnitConversions{ - - public static $groups = array('length','duration','angle'); - - public static $length = array( - 'm'=> 1, - 'cm'=> 0.01, - 'mm'=> 0.001, - 'in'=> 0.0254, - 'px'=> 0.000264583, // 0.0254 / 96, - 'pt'=> 0.000352778, // 0.0254 / 72, - 'pc'=> 0.004233333, // 0.0254 / 72 * 12 - ); - - public static $duration = array( - 's'=> 1, - 'ms'=> 0.001 - ); - - public static $angle = array( - 'rad' => 0.1591549430919, // 1/(2*M_PI), - 'deg' => 0.002777778, // 1/360, - 'grad'=> 0.0025, // 1/400, - 'turn'=> 1 - ); - -} - -/** - * Url - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Url extends Less_Tree{ - - public $attrs; - public $value; - public $currentFileInfo; - public $isEvald; - public $type = 'Url'; - - public function __construct($value, $currentFileInfo = null, $isEvald = null){ - $this->value = $value; - $this->currentFileInfo = $currentFileInfo; - $this->isEvald = $isEvald; - } - - public function accept( $visitor ){ - $this->value = $visitor->visitObj($this->value); - } - - /** - * @see Less_Tree::genCSS - */ - public function genCSS( $output ){ - $output->add( 'url(' ); - $this->value->genCSS( $output ); - $output->add( ')' ); - } - - /** - * @param Less_Functions $ctx - */ - public function compile($ctx){ - $val = $this->value->compile($ctx); - - if( !$this->isEvald ){ - // Add the base path if the URL is relative - if( Less_Parser::$options['relativeUrls'] - && $this->currentFileInfo - && is_string($val->value) - && Less_Environment::isPathRelative($val->value) - ){ - $rootpath = $this->currentFileInfo['uri_root']; - if ( !$val->quote ){ - $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath ); - } - $val->value = $rootpath . $val->value; - } - - $val->value = Less_Environment::normalizePath( $val->value); - } - - // Add cache buster if enabled - if( Less_Parser::$options['urlArgs'] ){ - if( !preg_match('/^\s*data:/',$val->value) ){ - $delimiter = strpos($val->value,'?') === false ? '?' : '&'; - $urlArgs = $delimiter . Less_Parser::$options['urlArgs']; - $hash_pos = strpos($val->value,'#'); - if( $hash_pos !== false ){ - $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0); - } else { - $val->value .= $urlArgs; - } - } - } - - return new Less_Tree_URL($val, $this->currentFileInfo, true); - } - -} - - -/** - * Value - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Value extends Less_Tree{ - - public $type = 'Value'; - public $value; - - public function __construct($value){ - $this->value = $value; - } - - public function accept($visitor) { - $this->value = $visitor->visitArray($this->value); - } - - public function compile($env){ - - $ret = array(); - $i = 0; - foreach($this->value as $i => $v){ - $ret[] = $v->compile($env); - } - if( $i > 0 ){ - return new Less_Tree_Value($ret); - } - return $ret[0]; - } - - /** - * @see Less_Tree::genCSS - */ - function genCSS( $output ){ - $len = count($this->value); - for($i = 0; $i < $len; $i++ ){ - $this->value[$i]->genCSS( $output ); - if( $i+1 < $len ){ - $output->add( Less_Environment::$_outputMap[','] ); - } - } - } - -} - - -/** - * Variable - * - * @package Less - * @subpackage tree - */ -class Less_Tree_Variable extends Less_Tree{ - - public $name; - public $index; - public $currentFileInfo; - public $evaluating = false; - public $type = 'Variable'; - - /** - * @param string $name - */ - public function __construct($name, $index = null, $currentFileInfo = null) { - $this->name = $name; - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - } - - public function compile($env) { - - if( $this->name[1] === '@' ){ - $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo); - $name = '@' . $v->compile($env)->value; - }else{ - $name = $this->name; - } - - if ($this->evaluating) { - throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo); - } - - $this->evaluating = true; - - foreach($env->frames as $frame){ - if( $v = $frame->variable($name) ){ - $r = $v->value->compile($env); - $this->evaluating = false; - return $r; - } - } - - throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo); - } - -} - - - -class Less_Tree_Mixin_Call extends Less_Tree{ - - public $selector; - public $arguments; - public $index; - public $currentFileInfo; - - public $important; - public $type = 'MixinCall'; - - /** - * less.js: tree.mixin.Call - * - */ - public function __construct($elements, $args, $index, $currentFileInfo, $important = false){ - $this->selector = new Less_Tree_Selector($elements); - $this->arguments = $args; - $this->index = $index; - $this->currentFileInfo = $currentFileInfo; - $this->important = $important; - } - - //function accept($visitor){ - // $this->selector = $visitor->visit($this->selector); - // $this->arguments = $visitor->visit($this->arguments); - //} - - - public function compile($env){ - - $rules = array(); - $match = false; - $isOneFound = false; - $candidates = array(); - $defaultUsed = false; - $conditionResult = array(); - - $args = array(); - foreach($this->arguments as $a){ - $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) ); - } - - foreach($env->frames as $frame){ - - $mixins = $frame->find($this->selector); - - if( !$mixins ){ - continue; - } - - $isOneFound = true; - $defNone = 0; - $defTrue = 1; - $defFalse = 2; - - // To make `default()` function independent of definition order we have two "subpasses" here. - // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`), - // and build candidate list with corresponding flags. Then, when we know all possible matches, - // we make a final decision. - - $mixins_len = count($mixins); - for( $m = 0; $m < $mixins_len; $m++ ){ - $mixin = $mixins[$m]; - - if( $this->IsRecursive( $env, $mixin ) ){ - continue; - } - - if( $mixin->matchArgs($args, $env) ){ - - $candidate = array('mixin' => $mixin, 'group' => $defNone); - - if( $mixin instanceof Less_Tree_Ruleset ){ - - for( $f = 0; $f < 2; $f++ ){ - Less_Tree_DefaultFunc::value($f); - $conditionResult[$f] = $mixin->matchCondition( $args, $env); - } - if( $conditionResult[0] || $conditionResult[1] ){ - if( $conditionResult[0] != $conditionResult[1] ){ - $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse; - } - - $candidates[] = $candidate; - } - }else{ - $candidates[] = $candidate; - } - - $match = true; - } - } - - Less_Tree_DefaultFunc::reset(); - - - $count = array(0, 0, 0); - for( $m = 0; $m < count($candidates); $m++ ){ - $count[ $candidates[$m]['group'] ]++; - } - - if( $count[$defNone] > 0 ){ - $defaultResult = $defFalse; - } else { - $defaultResult = $defTrue; - if( ($count[$defTrue] + $count[$defFalse]) > 1 ){ - throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' ); - } - } - - - $candidates_length = count($candidates); - $length_1 = ($candidates_length == 1); - - for( $m = 0; $m < $candidates_length; $m++){ - $candidate = $candidates[$m]['group']; - if( ($candidate === $defNone) || ($candidate === $defaultResult) ){ - try{ - $mixin = $candidates[$m]['mixin']; - if( !($mixin instanceof Less_Tree_Mixin_Definition) ){ - $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false); - $mixin->originalRuleset = $mixins[$m]->originalRuleset; - } - $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules); - } catch (Exception $e) { - //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']); - throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo); - } - } - } - - if( $match ){ - if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){ - Less_Tree::ReferencedArray($rules); - } - - return $rules; - } - } - - if( $isOneFound ){ - throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo); - - }else{ - throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index); - } - - } - - /** - * Format the args for use in exception messages - * - */ - private function Format($args){ - $message = array(); - if( $args ){ - foreach($args as $a){ - $argValue = ''; - if( $a['name'] ){ - $argValue .= $a['name'] . ':'; - } - if( is_object($a['value']) ){ - $argValue .= $a['value']->toCSS(); - }else{ - $argValue .= '???'; - } - $message[] = $argValue; - } - } - return implode(', ',$message); - } - - - /** - * Are we in a recursive mixin call? - * - * @return bool - */ - private function IsRecursive( $env, $mixin ){ - - foreach($env->frames as $recur_frame){ - if( !($mixin instanceof Less_Tree_Mixin_Definition) ){ - - if( $mixin === $recur_frame ){ - return true; - } - - if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){ - return true; - } - } - } - - return false; - } - -} - - - - -class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{ - public $name; - public $selectors; - public $params; - public $arity = 0; - public $rules; - public $lookups = array(); - public $required = 0; - public $frames = array(); - public $condition; - public $variadic; - public $type = 'MixinDefinition'; - - - // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition - public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){ - $this->name = $name; - $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name)))); - - $this->params = $params; - $this->condition = $condition; - $this->variadic = $variadic; - $this->rules = $rules; - - if( $params ){ - $this->arity = count($params); - foreach( $params as $p ){ - if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) { - $this->required++; - } - } - } - - $this->frames = $frames; - $this->SetRulesetIndex(); - } - - - - //function accept( $visitor ){ - // $this->params = $visitor->visit($this->params); - // $this->rules = $visitor->visit($this->rules); - // $this->condition = $visitor->visit($this->condition); - //} - - - public function toCSS(){ - return ''; - } - - // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams - public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){ - $frame = new Less_Tree_Ruleset(null, array()); - $params = $this->params; - $mixinEnv = null; - $argsLength = 0; - - if( $args ){ - $argsLength = count($args); - for($i = 0; $i < $argsLength; $i++ ){ - $arg = $args[$i]; - - if( $arg && $arg['name'] ){ - $isNamedFound = false; - - foreach($params as $j => $param){ - if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) { - $evaldArguments[$j] = $arg['value']->compile($env); - array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) ); - $isNamedFound = true; - break; - } - } - if ($isNamedFound) { - array_splice($args, $i, 1); - $i--; - $argsLength--; - continue; - } else { - throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found'); - } - } - } - } - - $argIndex = 0; - foreach($params as $i => $param){ - - if ( isset($evaldArguments[$i]) ){ continue; } - - $arg = null; - if( isset($args[$argIndex]) ){ - $arg = $args[$argIndex]; - } - - if (isset($param['name']) && $param['name']) { - - if( isset($param['variadic']) ){ - $varargs = array(); - for ($j = $argIndex; $j < $argsLength; $j++) { - $varargs[] = $args[$j]['value']->compile($env); - } - $expression = new Less_Tree_Expression($varargs); - array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env))); - }else{ - $val = ($arg && $arg['value']) ? $arg['value'] : false; - - if ($val) { - $val = $val->compile($env); - } else if ( isset($param['value']) ) { - - if( !$mixinEnv ){ - $mixinEnv = new Less_Environment(); - $mixinEnv->frames = array_merge( array($frame), $mixinFrames); - } - - $val = $param['value']->compile($mixinEnv); - $frame->resetCache(); - } else { - throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")"); - } - - array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val)); - $evaldArguments[$i] = $val; - } - } - - if ( isset($param['variadic']) && $args) { - for ($j = $argIndex; $j < $argsLength; $j++) { - $evaldArguments[$j] = $args[$j]['value']->compile($env); - } - } - $argIndex++; - } - - ksort($evaldArguments); - $evaldArguments = array_values($evaldArguments); - - return $frame; - } - - public function compile($env) { - if( $this->frames ){ - return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames ); - } - return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames ); - } - - public function evalCall($env, $args = NULL, $important = NULL) { - - Less_Environment::$mixin_stack++; - - $_arguments = array(); - - if( $this->frames ){ - $mixinFrames = array_merge($this->frames, $env->frames); - }else{ - $mixinFrames = $env->frames; - } - - $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments); - - $ex = new Less_Tree_Expression($_arguments); - array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env))); - - - $ruleset = new Less_Tree_Ruleset(null, $this->rules); - $ruleset->originalRuleset = $this->ruleset_id; - - - $ruleSetEnv = new Less_Environment(); - $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames ); - $ruleset = $ruleset->compile( $ruleSetEnv ); - - if( $important ){ - $ruleset = $ruleset->makeImportant(); - } - - Less_Environment::$mixin_stack--; - - return $ruleset; - } - - - public function matchCondition($args, $env) { - - if( !$this->condition ){ - return true; - } - - // set array to prevent error on array_merge - if(!is_array($this->frames)) { - $this->frames = array(); - } - - $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args ); - - $compile_env = new Less_Environment(); - $compile_env->frames = array_merge( - array($frame) // the parameter variables - , $this->frames // the parent namespace/mixin frames - , $env->frames // the current environment frames - ); - - $compile_env->functions = $env->functions; - - return (bool)$this->condition->compile($compile_env); - } - - public function matchArgs($args, $env = NULL){ - $argsLength = count($args); - - if( !$this->variadic ){ - if( $argsLength < $this->required ){ - return false; - } - if( $argsLength > count($this->params) ){ - return false; - } - }else{ - if( $argsLength < ($this->required - 1)){ - return false; - } - } - - $len = min($argsLength, $this->arity); - - for( $i = 0; $i < $len; $i++ ){ - if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){ - if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){ - return false; - } - } - } - - return true; - } - -} - - -/** - * Extend Finder Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_Visitor_extendFinder extends Less_Visitor{ - - public $contexts = array(); - public $allExtendsStack; - public $foundExtends; - - public function __construct(){ - $this->contexts = array(); - $this->allExtendsStack = array(array()); - parent::__construct(); - } - - /** - * @param Less_Tree_Ruleset $root - */ - public function run($root){ - $root = $this->visitObj($root); - $root->allExtends =& $this->allExtendsStack[0]; - return $root; - } - - public function visitRule($ruleNode, &$visitDeeper ){ - $visitDeeper = false; - } - - public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ - $visitDeeper = false; - } - - public function visitRuleset($rulesetNode){ - - if( $rulesetNode->root ){ - return; - } - - $allSelectorsExtendList = array(); - - // get &:extend(.a); rules which apply to all selectors in this ruleset - if( $rulesetNode->rules ){ - foreach($rulesetNode->rules as $rule){ - if( $rule instanceof Less_Tree_Extend ){ - $allSelectorsExtendList[] = $rule; - $rulesetNode->extendOnEveryPath = true; - } - } - } - - - // now find every selector and apply the extends that apply to all extends - // and the ones which apply to an individual extend - foreach($rulesetNode->paths as $selectorPath){ - $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1]; - - $j = 0; - foreach($selector->extendList as $extend){ - $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j); - } - foreach($allSelectorsExtendList as $extend){ - $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j); - } - } - - $this->contexts[] = $rulesetNode->selectors; - } - - public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){ - $this->foundExtends = true; - $extend = clone $extend; - $extend->findSelfSelectors( $selectorPath ); - $extend->ruleset = $rulesetNode; - if( $j === 0 ){ - $extend->firstExtendOnThisSelectorPath = true; - } - - $end_key = count($this->allExtendsStack)-1; - $this->allExtendsStack[$end_key][] = $extend; - $j++; - } - - - public function visitRulesetOut( $rulesetNode ){ - if( !is_object($rulesetNode) || !$rulesetNode->root ){ - array_pop($this->contexts); - } - } - - public function visitMedia( $mediaNode ){ - $mediaNode->allExtends = array(); - $this->allExtendsStack[] =& $mediaNode->allExtends; - } - - public function visitMediaOut(){ - array_pop($this->allExtendsStack); - } - - public function visitDirective( $directiveNode ){ - $directiveNode->allExtends = array(); - $this->allExtendsStack[] =& $directiveNode->allExtends; - } - - public function visitDirectiveOut(){ - array_pop($this->allExtendsStack); - } -} - - - - -/* -class Less_Visitor_import extends Less_VisitorReplacing{ - - public $_visitor; - public $_importer; - public $importCount; - - function __construct( $evalEnv ){ - $this->env = $evalEnv; - $this->importCount = 0; - parent::__construct(); - } - - - function run( $root ){ - $root = $this->visitObj($root); - $this->isFinished = true; - - //if( $this->importCount === 0) { - // $this->_finish(); - //} - } - - function visitImport($importNode, &$visitDeeper ){ - $importVisitor = $this; - $inlineCSS = $importNode->options['inline']; - - if( !$importNode->css || $inlineCSS ){ - $evaldImportNode = $importNode->compileForImport($this->env); - - if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){ - $importNode = $evaldImportNode; - $this->importCount++; - $env = clone $this->env; - - if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){ - $env->importMultiple = true; - } - - //get path & uri - $path_and_uri = null; - if( is_callable(Less_Parser::$options['import_callback']) ){ - $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode); - } - - if( !$path_and_uri ){ - $path_and_uri = $importNode->PathAndUri(); - } - - if( $path_and_uri ){ - list($full_path, $uri) = $path_and_uri; - }else{ - $full_path = $uri = $importNode->getPath(); - } - - - //import once - if( $importNode->skip( $full_path, $env) ){ - return array(); - } - - if( $importNode->options['inline'] ){ - //todo needs to reference css file not import - //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true ); - - Less_Parser::AddParsedFile($full_path); - $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true ); - - if( $importNode->features ){ - return new Less_Tree_Media( array($contents), $importNode->features->value ); - } - - return array( $contents ); - } - - - // css ? - if( $importNode->css ){ - $features = ( $importNode->features ? $importNode->features->compile($env) : null ); - return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index); - } - - return $importNode->ParseImport( $full_path, $uri, $env ); - } - - } - - $visitDeeper = false; - return $importNode; - } - - - function visitRule( $ruleNode, &$visitDeeper ){ - $visitDeeper = false; - return $ruleNode; - } - - function visitDirective($directiveNode, $visitArgs){ - array_unshift($this->env->frames,$directiveNode); - return $directiveNode; - } - - function visitDirectiveOut($directiveNode) { - array_shift($this->env->frames); - } - - function visitMixinDefinition($mixinDefinitionNode, $visitArgs) { - array_unshift($this->env->frames,$mixinDefinitionNode); - return $mixinDefinitionNode; - } - - function visitMixinDefinitionOut($mixinDefinitionNode) { - array_shift($this->env->frames); - } - - function visitRuleset($rulesetNode, $visitArgs) { - array_unshift($this->env->frames,$rulesetNode); - return $rulesetNode; - } - - function visitRulesetOut($rulesetNode) { - array_shift($this->env->frames); - } - - function visitMedia($mediaNode, $visitArgs) { - array_unshift($this->env->frames, $mediaNode->ruleset); - return $mediaNode; - } - - function visitMediaOut($mediaNode) { - array_shift($this->env->frames); - } - -} -*/ - - - - -/** - * Join Selector Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_Visitor_joinSelector extends Less_Visitor{ - - public $contexts = array( array() ); - - /** - * @param Less_Tree_Ruleset $root - */ - public function run( $root ){ - return $this->visitObj($root); - } - - public function visitRule( $ruleNode, &$visitDeeper ){ - $visitDeeper = false; - } - - public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ - $visitDeeper = false; - } - - public function visitRuleset( $rulesetNode ){ - - $paths = array(); - - if( !$rulesetNode->root ){ - $selectors = array(); - - if( $rulesetNode->selectors && $rulesetNode->selectors ){ - foreach($rulesetNode->selectors as $selector){ - if( $selector->getIsOutput() ){ - $selectors[] = $selector; - } - } - } - - if( !$selectors ){ - $rulesetNode->selectors = null; - $rulesetNode->rules = null; - }else{ - $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1]; - $paths = $rulesetNode->joinSelectors( $context, $selectors); - } - - $rulesetNode->paths = $paths; - } - - $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths - } - - public function visitRulesetOut(){ - array_pop($this->contexts); - } - - public function visitMedia($mediaNode) { - $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1]; - - if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){ - $mediaNode->rules[0]->root = true; - } - } - -} - - - -/** - * Process Extends Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_Visitor_processExtends extends Less_Visitor{ - - public $allExtendsStack; - - /** - * @param Less_Tree_Ruleset $root - */ - public function run( $root ){ - $extendFinder = new Less_Visitor_extendFinder(); - $extendFinder->run( $root ); - if( !$extendFinder->foundExtends){ - return $root; - } - - $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends); - - $this->allExtendsStack = array(); - $this->allExtendsStack[] = &$root->allExtends; - - return $this->visitObj( $root ); - } - - private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){ - // - // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting - // the selector we would do normally, but we are also adding an extend with the same target selector - // this means this new extend can then go and alter other extends - // - // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors - // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if - // we look at each selector at a time, as is done in visitRuleset - - $extendsToAdd = array(); - - - //loop through comparing every extend with every target extend. - // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place - // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one - // and the second is the target. - // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the - // case when processing media queries - for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){ - for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){ - - $extend = $extendsList[$extendIndex]; - $targetExtend = $extendsListTarget[$targetExtendIndex]; - - // look for circular references - if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){ - continue; - } - - // find a match in the target extends self selector (the bit before :extend) - $selectorPath = array( $targetExtend->selfSelectors[0] ); - $matches = $this->findMatch( $extend, $selectorPath); - - - if( $matches ){ - - // we found a match, so for each self selector.. - foreach($extend->selfSelectors as $selfSelector ){ - - - // process the extend as usual - $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector); - - // but now we create a new extend from it - $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0); - $newExtend->selfSelectors = $newSelector; - - // add the extend onto the list of extends for that selector - end($newSelector)->extendList = array($newExtend); - //$newSelector[ count($newSelector)-1]->extendList = array($newExtend); - - // record that we need to add it. - $extendsToAdd[] = $newExtend; - $newExtend->ruleset = $targetExtend->ruleset; - - //remember its parents for circular references - $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids); - - // only process the selector once.. if we have :extend(.a,.b) then multiple - // extends will look at the same selector path, so when extending - // we know that any others will be duplicates in terms of what is added to the css - if( $targetExtend->firstExtendOnThisSelectorPath ){ - $newExtend->firstExtendOnThisSelectorPath = true; - $targetExtend->ruleset->paths[] = $newSelector; - } - } - } - } - } - - if( $extendsToAdd ){ - // try to detect circular references to stop a stack overflow. - // may no longer be needed. $this->extendChainCount++; - if( $iterationCount > 100) { - - try{ - $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS(); - $selectorTwo = $extendsToAdd[0]->selector->toCSS(); - }catch(Exception $e){ - $selectorOne = "{unable to calculate}"; - $selectorTwo = "{unable to calculate}"; - } - - throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")"); - } - - // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e... - $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1); - } - - return array_merge($extendsList, $extendsToAdd); - } - - - protected function visitRule( $ruleNode, &$visitDeeper ){ - $visitDeeper = false; - } - - protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){ - $visitDeeper = false; - } - - protected function visitSelector( $selectorNode, &$visitDeeper ){ - $visitDeeper = false; - } - - protected function visitRuleset($rulesetNode){ - - - if( $rulesetNode->root ){ - return; - } - - $allExtends = end($this->allExtendsStack); - $paths_len = count($rulesetNode->paths); - - // look at each selector path in the ruleset, find any extend matches and then copy, find and replace - foreach($allExtends as $allExtend){ - for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){ - - // extending extends happens initially, before the main pass - if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){ - continue; - } - - $selectorPath = $rulesetNode->paths[$pathIndex]; - - if( end($selectorPath)->extendList ){ - continue; - } - - $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath); - - } - } - } - - - private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){ - $matches = $this->findMatch($extend, $selectorPath); - - if( $matches ){ - foreach($extend->selfSelectors as $selfSelector ){ - $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector); - } - } - } - - - - private function findMatch($extend, $haystackSelectorPath ){ - - - if( !$this->HasMatches($extend, $haystackSelectorPath) ){ - return false; - } - - - // - // look through the haystack selector path to try and find the needle - extend.selector - // returns an array of selector matches that can then be replaced - // - $needleElements = $extend->selector->elements; - $potentialMatches = array(); - $potentialMatches_len = 0; - $potentialMatch = null; - $matches = array(); - - - - // loop through the haystack elements - $haystack_path_len = count($haystackSelectorPath); - for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){ - $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex]; - - $haystack_elements_len = count($hackstackSelector->elements); - for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){ - - $haystackElement = $hackstackSelector->elements[$hackstackElementIndex]; - - // if we allow elements before our match we can add a potential match every time. otherwise only at the first element. - if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){ - $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator); - $potentialMatches_len++; - } - - for($i = 0; $i < $potentialMatches_len; $i++ ){ - - $potentialMatch = &$potentialMatches[$i]; - $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ); - - - // if we are still valid and have finished, test whether we have elements after and whether these are allowed - if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){ - $potentialMatch['finished'] = true; - - if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){ - $potentialMatch = null; - } - } - - // if null we remove, if not, we are still valid, so either push as a valid match or continue - if( $potentialMatch ){ - if( $potentialMatch['finished'] ){ - $potentialMatch['length'] = $extend->selector->elements_len; - $potentialMatch['endPathIndex'] = $haystackSelectorIndex; - $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match - $potentialMatches = array(); // we don't allow matches to overlap, so start matching again - $potentialMatches_len = 0; - $matches[] = $potentialMatch; - } - continue; - } - - array_splice($potentialMatches, $i, 1); - $potentialMatches_len--; - $i--; - } - } - } - - return $matches; - } - - - // Before going through all the nested loops, lets check to see if a match is possible - // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s - private function HasMatches($extend, $haystackSelectorPath){ - - if( !$extend->selector->cacheable ){ - return true; - } - - $first_el = $extend->selector->_oelements[0]; - - foreach($haystackSelectorPath as $hackstackSelector){ - if( !$hackstackSelector->cacheable ){ - return true; - } - - if( in_array($first_el, $hackstackSelector->_oelements) ){ - return true; - } - } - - return false; - } - - - /** - * @param integer $hackstackElementIndex - */ - private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){ - - - if( $potentialMatch['matched'] > 0 ){ - - // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't - // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out - // what the resulting combinator will be - $targetCombinator = $haystackElement->combinator; - if( $targetCombinator === '' && $hackstackElementIndex === 0 ){ - $targetCombinator = ' '; - } - - if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){ - return null; - } - } - - // if we don't match, null our match to indicate failure - if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){ - return null; - } - - $potentialMatch['finished'] = false; - $potentialMatch['matched']++; - - return $potentialMatch; - } - - - private function isElementValuesEqual( $elementValue1, $elementValue2 ){ - - if( $elementValue1 === $elementValue2 ){ - return true; - } - - if( is_string($elementValue1) || is_string($elementValue2) ) { - return false; - } - - if( $elementValue1 instanceof Less_Tree_Attribute ){ - return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 ); - } - - $elementValue1 = $elementValue1->value; - if( $elementValue1 instanceof Less_Tree_Selector ){ - return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 ); - } - - return false; - } - - - /** - * @param Less_Tree_Selector $elementValue1 - */ - private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){ - - $elementValue2 = $elementValue2->value; - if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){ - return false; - } - - for( $i = 0; $i < $elementValue1->elements_len; $i++ ){ - - if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){ - if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){ - return false; - } - } - - if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){ - return false; - } - } - - return true; - } - - - /** - * @param Less_Tree_Attribute $elementValue1 - */ - private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){ - - if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){ - return false; - } - - if( !$elementValue1->value || !$elementValue2->value ){ - if( $elementValue1->value || $elementValue2->value ) { - return false; - } - return true; - } - - $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value ); - $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value ); - - return $elementValue1 === $elementValue2; - } - - - private function extendSelector($matches, $selectorPath, $replacementSelector){ - - //for a set of matches, replace each match with the replacement selector - - $currentSelectorPathIndex = 0; - $currentSelectorPathElementIndex = 0; - $path = array(); - $selectorPath_len = count($selectorPath); - - for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){ - - - $match = $matches[$matchIndex]; - $selector = $selectorPath[ $match['pathIndex'] ]; - - $firstElement = new Less_Tree_Element( - $match['initialCombinator'], - $replacementSelector->elements[0]->value, - $replacementSelector->elements[0]->index, - $replacementSelector->elements[0]->currentFileInfo - ); - - if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){ - $last_path = end($path); - $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex)); - $currentSelectorPathElementIndex = 0; - $currentSelectorPathIndex++; - } - - $newElements = array_merge( - array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice - , array($firstElement) - , array_slice($replacementSelector->elements,1) - ); - - if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){ - $last_key = count($path)-1; - $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements); - }else{ - $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] )); - $path[] = new Less_Tree_Selector( $newElements ); - } - - $currentSelectorPathIndex = $match['endPathIndex']; - $currentSelectorPathElementIndex = $match['endPathElementIndex']; - if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){ - $currentSelectorPathElementIndex = 0; - $currentSelectorPathIndex++; - } - } - - if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){ - $last_path = end($path); - $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex)); - $currentSelectorPathIndex++; - } - - $slice_len = $selectorPath_len - $currentSelectorPathIndex; - $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len)); - - return $path; - } - - - protected function visitMedia( $mediaNode ){ - $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) ); - $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends); - } - - protected function visitMediaOut(){ - array_pop( $this->allExtendsStack ); - } - - protected function visitDirective( $directiveNode ){ - $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) ); - $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends); - } - - protected function visitDirectiveOut(){ - array_pop($this->allExtendsStack); - } - -} - -/** - * toCSS Visitor - * - * @package Less - * @subpackage visitor - */ -class Less_Visitor_toCSS extends Less_VisitorReplacing{ - - private $charset; - - public function __construct(){ - parent::__construct(); - } - - /** - * @param Less_Tree_Ruleset $root - */ - public function run( $root ){ - return $this->visitObj($root); - } - - public function visitRule( $ruleNode ){ - if( $ruleNode->variable ){ - return array(); - } - return $ruleNode; - } - - public function visitMixinDefinition($mixinNode){ - // mixin definitions do not get eval'd - this means they keep state - // so we have to clear that state here so it isn't used if toCSS is called twice - $mixinNode->frames = array(); - return array(); - } - - public function visitExtend(){ - return array(); - } - - public function visitComment( $commentNode ){ - if( $commentNode->isSilent() ){ - return array(); - } - return $commentNode; - } - - public function visitMedia( $mediaNode, &$visitDeeper ){ - $mediaNode->accept($this); - $visitDeeper = false; - - if( !$mediaNode->rules ){ - return array(); - } - return $mediaNode; - } - - public function visitDirective( $directiveNode ){ - if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){ - return array(); - } - if( $directiveNode->name === '@charset' ){ - // Only output the debug info together with subsequent @charset definitions - // a comment (or @media statement) before the actual @charset directive would - // be considered illegal css as it has to be on the first line - if( isset($this->charset) && $this->charset ){ - - //if( $directiveNode->debugInfo ){ - // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n"); - // $comment->debugInfo = $directiveNode->debugInfo; - // return $this->visit($comment); - //} - - - return array(); - } - $this->charset = true; - } - return $directiveNode; - } - - public function checkPropertiesInRoot( $rulesetNode ){ - - if( !$rulesetNode->firstRoot ){ - return; - } - - foreach($rulesetNode->rules as $ruleNode){ - if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){ - $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null); - throw new Less_Exception_Compiler($msg); - } - } - } - - - public function visitRuleset( $rulesetNode, &$visitDeeper ){ - - $visitDeeper = false; - - $this->checkPropertiesInRoot( $rulesetNode ); - - if( $rulesetNode->root ){ - return $this->visitRulesetRoot( $rulesetNode ); - } - - $rulesets = array(); - $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode); - - - // Compile rules and rulesets - $nodeRuleCnt = count($rulesetNode->rules); - for( $i = 0; $i < $nodeRuleCnt; ){ - $rule = $rulesetNode->rules[$i]; - - if( property_exists($rule,'rules') ){ - // visit because we are moving them out from being a child - $rulesets[] = $this->visitObj($rule); - array_splice($rulesetNode->rules,$i,1); - $nodeRuleCnt--; - continue; - } - $i++; - } - - - // accept the visitor to remove rules and refactor itself - // then we can decide now whether we want it or not - if( $nodeRuleCnt > 0 ){ - $rulesetNode->accept($this); - - if( $rulesetNode->rules ){ - - if( count($rulesetNode->rules) > 1 ){ - $this->_mergeRules( $rulesetNode->rules ); - $this->_removeDuplicateRules( $rulesetNode->rules ); - } - - // now decide whether we keep the ruleset - if( $rulesetNode->paths ){ - //array_unshift($rulesets, $rulesetNode); - array_splice($rulesets,0,0,array($rulesetNode)); - } - } - - } - - - if( count($rulesets) === 1 ){ - return $rulesets[0]; - } - return $rulesets; - } - - - /** - * Helper function for visitiRuleset - * - * return array|Less_Tree_Ruleset - */ - private function visitRulesetRoot( $rulesetNode ){ - $rulesetNode->accept( $this ); - if( $rulesetNode->firstRoot || $rulesetNode->rules ){ - return $rulesetNode; - } - return array(); - } - - - /** - * Helper function for visitRuleset() - * - * @return array - */ - private function visitRulesetPaths($rulesetNode){ - - $paths = array(); - foreach($rulesetNode->paths as $p){ - if( $p[0]->elements[0]->combinator === ' ' ){ - $p[0]->elements[0]->combinator = ''; - } - - foreach($p as $pi){ - if( $pi->getIsReferenced() && $pi->getIsOutput() ){ - $paths[] = $p; - break; - } - } - } - - return $paths; - } - - protected function _removeDuplicateRules( &$rules ){ - // remove duplicates - $ruleCache = array(); - for( $i = count($rules)-1; $i >= 0 ; $i-- ){ - $rule = $rules[$i]; - if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){ - - if( !isset($ruleCache[$rule->name]) ){ - $ruleCache[$rule->name] = $rule; - }else{ - $ruleList =& $ruleCache[$rule->name]; - - if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){ - $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() ); - } - - $ruleCSS = $rule->toCSS(); - if( array_search($ruleCSS,$ruleList) !== false ){ - array_splice($rules,$i,1); - }else{ - $ruleList[] = $ruleCSS; - } - } - } - } - } - - protected function _mergeRules( &$rules ){ - $groups = array(); - - //obj($rules); - - $rules_len = count($rules); - for( $i = 0; $i < $rules_len; $i++ ){ - $rule = $rules[$i]; - - if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){ - - $key = $rule->name; - if( $rule->important ){ - $key .= ',!'; - } - - if( !isset($groups[$key]) ){ - $groups[$key] = array(); - }else{ - array_splice($rules, $i--, 1); - $rules_len--; - } - - $groups[$key][] = $rule; - } - } - - - foreach($groups as $parts){ - - if( count($parts) > 1 ){ - $rule = $parts[0]; - $spacedGroups = array(); - $lastSpacedGroup = array(); - $parts_mapped = array(); - foreach($parts as $p){ - if( $p->merge === '+' ){ - if( $lastSpacedGroup ){ - $spacedGroups[] = self::toExpression($lastSpacedGroup); - } - $lastSpacedGroup = array(); - } - $lastSpacedGroup[] = $p; - } - - $spacedGroups[] = self::toExpression($lastSpacedGroup); - $rule->value = self::toValue($spacedGroups); - } - } - - } - - public static function toExpression($values){ - $mapped = array(); - foreach($values as $p){ - $mapped[] = $p->value; - } - return new Less_Tree_Expression( $mapped ); - } - - public static function toValue($values){ - //return new Less_Tree_Value($values); ?? - - $mapped = array(); - foreach($values as $p){ - $mapped[] = $p; - } - return new Less_Tree_Value($mapped); - } -} - - - -/** - * Parser Exception - * - * @package Less - * @subpackage exception - */ -class Less_Exception_Parser extends Exception{ - - /** - * The current file - * - * @var Less_ImportedFile - */ - public $currentFile; - - /** - * The current parser index - * - * @var integer - */ - public $index; - - protected $input; - - protected $details = array(); - - - /** - * Constructor - * - * @param string $message - * @param Exception $previous Previous exception - * @param integer $index The current parser index - * @param Less_FileInfo|string $currentFile The file - * @param integer $code The exception code - */ - public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){ - - if (PHP_VERSION_ID < 50300) { - $this->previous = $previous; - parent::__construct($message, $code); - } else { - parent::__construct($message, $code, $previous); - } - - $this->currentFile = $currentFile; - $this->index = $index; - - $this->genMessage(); - } - - - protected function getInput(){ - - if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){ - $this->input = file_get_contents( $this->currentFile['filename'] ); - } - } - - - - /** - * Converts the exception to string - * - * @return string - */ - public function genMessage(){ - - if( $this->currentFile && $this->currentFile['filename'] ){ - $this->message .= ' in '.basename($this->currentFile['filename']); - } - - if( $this->index !== null ){ - $this->getInput(); - if( $this->input ){ - $line = self::getLineNumber(); - $this->message .= ' on line '.$line.', column '.self::getColumn(); - - $lines = explode("\n",$this->input); - - $count = count($lines); - $start_line = max(0, $line-3); - $last_line = min($count, $start_line+6); - $num_len = strlen($last_line); - for( $i = $start_line; $i < $last_line; $i++ ){ - $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i]; - } - } - } - - } - - /** - * Returns the line number the error was encountered - * - * @return integer - */ - public function getLineNumber(){ - if( $this->index ){ - // https://bugs.php.net/bug.php?id=49790 - if (ini_get("mbstring.func_overload")) { - return substr_count(substr($this->input, 0, $this->index), "\n") + 1; - } else { - return substr_count($this->input, "\n", 0, $this->index) + 1; - } - } - return 1; - } - - - /** - * Returns the column the error was encountered - * - * @return integer - */ - public function getColumn(){ - - $part = substr($this->input, 0, $this->index); - $pos = strrpos($part,"\n"); - return $this->index - $pos; - } - -} - - -/** - * Chunk Exception - * - * @package Less - * @subpackage exception - */ -class Less_Exception_Chunk extends Less_Exception_Parser{ - - - protected $parserCurrentIndex = 0; - - protected $emitFrom = 0; - - protected $input_len; - - - /** - * Constructor - * - * @param string $input - * @param Exception $previous Previous exception - * @param integer $index The current parser index - * @param Less_FileInfo|string $currentFile The file - * @param integer $code The exception code - */ - public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){ - - $this->message = 'ParseError: Unexpected input'; //default message - - $this->index = $index; - - $this->currentFile = $currentFile; - - $this->input = $input; - $this->input_len = strlen($input); - - $this->Chunks(); - $this->genMessage(); - } - - - /** - * See less.js chunks() - * We don't actually need the chunks - * - */ - protected function Chunks(){ - $level = 0; - $parenLevel = 0; - $lastMultiCommentEndBrace = null; - $lastOpening = null; - $lastMultiComment = null; - $lastParen = null; - - for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){ - $cc = $this->CharCode($this->parserCurrentIndex); - if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) { - // a-z or whitespace - continue; - } - - switch ($cc) { - - // ( - case 40: - $parenLevel++; - $lastParen = $this->parserCurrentIndex; - continue; - - // ) - case 41: - $parenLevel--; - if( $parenLevel < 0 ){ - return $this->fail("missing opening `(`"); - } - continue; - - // ; - case 59: - //if (!$parenLevel) { $this->emitChunk(); } - continue; - - // { - case 123: - $level++; - $lastOpening = $this->parserCurrentIndex; - continue; - - // } - case 125: - $level--; - if( $level < 0 ){ - return $this->fail("missing opening `{`"); - - } - //if (!$level && !$parenLevel) { $this->emitChunk(); } - continue; - // \ - case 92: - if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; } - return $this->fail("unescaped `\\`"); - - // ", ' and ` - case 34: - case 39: - case 96: - $matched = 0; - $currentChunkStartIndex = $this->parserCurrentIndex; - for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) { - $cc2 = $this->CharCode($this->parserCurrentIndex); - if ($cc2 > 96) { continue; } - if ($cc2 == $cc) { $matched = 1; break; } - if ($cc2 == 92) { // \ - if ($this->parserCurrentIndex == $this->input_len - 1) { - return $this->fail("unescaped `\\`"); - } - $this->parserCurrentIndex++; - } - } - if ($matched) { continue; } - return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex); - - // /, check for comment - case 47: - if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; } - $cc2 = $this->CharCode($this->parserCurrentIndex+1); - if ($cc2 == 47) { - // //, find lnfeed - for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) { - $cc2 = $this->CharCode($this->parserCurrentIndex); - if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; } - } - } else if ($cc2 == 42) { - // /*, find */ - $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex; - for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) { - $cc2 = $this->CharCode($this->parserCurrentIndex); - if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; } - if ($cc2 != 42) { continue; } - if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; } - } - if ($this->parserCurrentIndex == $this->input_len - 1) { - return $this->fail("missing closing `*/`", $currentChunkStartIndex); - } - } - continue; - - // *, check for unmatched */ - case 42: - if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) { - return $this->fail("unmatched `/*`"); - } - continue; - } - } - - if( $level !== 0 ){ - if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){ - return $this->fail("missing closing `}` or `*/`", $lastOpening); - } else { - return $this->fail("missing closing `}`", $lastOpening); - } - } else if ( $parenLevel !== 0 ){ - return $this->fail("missing closing `)`", $lastParen); - } - - - //chunk didn't fail - - - //$this->emitChunk(true); - } - - public function CharCode($pos){ - return ord($this->input[$pos]); - } - - - public function fail( $msg, $index = null ){ - - if( !$index ){ - $this->index = $this->parserCurrentIndex; - }else{ - $this->index = $index; - } - $this->message = 'ParseError: '.$msg; - } - - - /* - function emitChunk( $force = false ){ - $len = $this->parserCurrentIndex - $this->emitFrom; - if ((($len < 512) && !$force) || !$len) { - return; - } - $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom ); - $this->emitFrom = $this->parserCurrentIndex + 1; - } - */ - -} - - -/** - * Compiler Exception - * - * @package Less - * @subpackage exception - */ -class Less_Exception_Compiler extends Less_Exception_Parser{ - -} - -/** - * Parser output with source map - * - * @package Less - * @subpackage Output - */ -class Less_Output_Mapped extends Less_Output { - - /** - * The source map generator - * - * @var Less_SourceMap_Generator - */ - protected $generator; - - /** - * Current line - * - * @var integer - */ - protected $lineNumber = 0; - - /** - * Current column - * - * @var integer - */ - protected $column = 0; - - /** - * Array of contents map (file and its content) - * - * @var array - */ - protected $contentsMap = array(); - - /** - * Constructor - * - * @param array $contentsMap Array of filename to contents map - * @param Less_SourceMap_Generator $generator - */ - public function __construct(array $contentsMap, $generator){ - $this->contentsMap = $contentsMap; - $this->generator = $generator; - } - - /** - * Adds a chunk to the stack - * The $index for less.php may be different from less.js since less.php does not chunkify inputs - * - * @param string $chunk - * @param string $fileInfo - * @param integer $index - * @param mixed $mapLines - */ - public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){ - - //ignore adding empty strings - if( $chunk === '' ){ - return; - } - - - $sourceLines = array(); - $sourceColumns = ' '; - - - if( $fileInfo ){ - - $url = $fileInfo['currentUri']; - - if( isset($this->contentsMap[$url]) ){ - $inputSource = substr($this->contentsMap[$url], 0, $index); - $sourceLines = explode("\n", $inputSource); - $sourceColumns = end($sourceLines); - }else{ - throw new Exception('Filename '.$url.' not in contentsMap'); - } - - } - - $lines = explode("\n", $chunk); - $columns = end($lines); - - if($fileInfo){ - - if(!$mapLines){ - $this->generator->addMapping( - $this->lineNumber + 1, // generated_line - $this->column, // generated_column - count($sourceLines), // original_line - strlen($sourceColumns), // original_column - $fileInfo - ); - }else{ - for($i = 0, $count = count($lines); $i < $count; $i++){ - $this->generator->addMapping( - $this->lineNumber + $i + 1, // generated_line - $i === 0 ? $this->column : 0, // generated_column - count($sourceLines) + $i, // original_line - $i === 0 ? strlen($sourceColumns) : 0, // original_column - $fileInfo - ); - } - } - } - - if(count($lines) === 1){ - $this->column += strlen($columns); - }else{ - $this->lineNumber += count($lines) - 1; - $this->column = strlen($columns); - } - - // add only chunk - parent::add($chunk); - } - -} - -/** - * Encode / Decode Base64 VLQ. - * - * @package Less - * @subpackage SourceMap - */ -class Less_SourceMap_Base64VLQ { - - /** - * Shift - * - * @var integer - */ - private $shift = 5; - - /** - * Mask - * - * @var integer - */ - private $mask = 0x1F; // == (1 << shift) == 0b00011111 - - /** - * Continuation bit - * - * @var integer - */ - private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000 - - /** - * Char to integer map - * - * @var array - */ - private $charToIntMap = array( - 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6, - 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13, - 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20, - 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27, - 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34, - 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41, - 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48, - 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56, - 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63, - ); - - /** - * Integer to char map - * - * @var array - */ - private $intToCharMap = array( - 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G', - 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N', - 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U', - 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b', - 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i', - 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p', - 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w', - 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3', - 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+', - 63 => '/', - ); - - /** - * Constructor - */ - public function __construct(){ - // I leave it here for future reference - // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char) - // { - // $this->charToIntMap[$char] = $i; - // $this->intToCharMap[$i] = $char; - // } - } - - /** - * Convert from a two-complement value to a value where the sign bit is - * is placed in the least significant bit. For example, as decimals: - * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary) - * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary) - * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297, - * even on a 64 bit machine. - * @param string $aValue - */ - public function toVLQSigned($aValue){ - return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0); - } - - /** - * Convert to a two-complement value from a value where the sign bit is - * is placed in the least significant bit. For example, as decimals: - * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1 - * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2 - * We assume that the value was generated with a 32 bit machine in mind. - * Hence - * 1 becomes -2147483648 - * even on a 64 bit machine. - * @param integer $aValue - */ - public function fromVLQSigned($aValue){ - return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1); - } - - /** - * Return the base 64 VLQ encoded value. - * - * @param string $aValue The value to encode - * @return string The encoded value - */ - public function encode($aValue){ - $encoded = ''; - $vlq = $this->toVLQSigned($aValue); - do - { - $digit = $vlq & $this->mask; - $vlq = $this->zeroFill($vlq, $this->shift); - if($vlq > 0){ - $digit |= $this->continuationBit; - } - $encoded .= $this->base64Encode($digit); - } while($vlq > 0); - - return $encoded; - } - - /** - * Return the value decoded from base 64 VLQ. - * - * @param string $encoded The encoded value to decode - * @return integer The decoded value - */ - public function decode($encoded){ - $vlq = 0; - $i = 0; - do - { - $digit = $this->base64Decode($encoded[$i]); - $vlq |= ($digit & $this->mask) << ($i * $this->shift); - $i++; - } while($digit & $this->continuationBit); - - return $this->fromVLQSigned($vlq); - } - - /** - * Right shift with zero fill. - * - * @param integer $a number to shift - * @param integer $b number of bits to shift - * @return integer - */ - public function zeroFill($a, $b){ - return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1)); - } - - /** - * Encode single 6-bit digit as base64. - * - * @param integer $number - * @return string - * @throws Exception If the number is invalid - */ - public function base64Encode($number){ - if($number < 0 || $number > 63){ - throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number)); - } - return $this->intToCharMap[$number]; - } - - /** - * Decode single 6-bit digit from base64 - * - * @param string $char - * @return number - * @throws Exception If the number is invalid - */ - public function base64Decode($char){ - if(!array_key_exists($char, $this->charToIntMap)){ - throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char)); - } - return $this->charToIntMap[$char]; - } - -} - - -/** - * Source map generator - * - * @package Less - * @subpackage Output - */ -class Less_SourceMap_Generator extends Less_Configurable { - - /** - * What version of source map does the generator generate? - */ - const VERSION = 3; - - /** - * Array of default options - * - * @var array - */ - protected $defaultOptions = array( - // an optional source root, useful for relocating source files - // on a server or removing repeated values in the 'sources' entry. - // This value is prepended to the individual entries in the 'source' field. - 'sourceRoot' => '', - - // an optional name of the generated code that this source map is associated with. - 'sourceMapFilename' => null, - - // url of the map - 'sourceMapURL' => null, - - // absolute path to a file to write the map to - 'sourceMapWriteTo' => null, - - // output source contents? - 'outputSourceFiles' => false, - - // base path for filename normalization - 'sourceMapRootpath' => '', - - // base path for filename normalization - 'sourceMapBasepath' => '' - ); - - /** - * The base64 VLQ encoder - * - * @var Less_SourceMap_Base64VLQ - */ - protected $encoder; - - /** - * Array of mappings - * - * @var array - */ - protected $mappings = array(); - - /** - * The root node - * - * @var Less_Tree_Ruleset - */ - protected $root; - - /** - * Array of contents map - * - * @var array - */ - protected $contentsMap = array(); - - /** - * File to content map - * - * @var array - */ - protected $sources = array(); - protected $source_keys = array(); - - /** - * Constructor - * - * @param Less_Tree_Ruleset $root The root node - * @param array $options Array of options - */ - public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){ - $this->root = $root; - $this->contentsMap = $contentsMap; - $this->encoder = new Less_SourceMap_Base64VLQ(); - - $this->SetOptions($options); - - $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true); - $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true); - } - - /** - * Generates the CSS - * - * @return string - */ - public function generateCSS(){ - $output = new Less_Output_Mapped($this->contentsMap, $this); - - // catch the output - $this->root->genCSS($output); - - - $sourceMapUrl = $this->getOption('sourceMapURL'); - $sourceMapFilename = $this->getOption('sourceMapFilename'); - $sourceMapContent = $this->generateJson(); - $sourceMapWriteTo = $this->getOption('sourceMapWriteTo'); - - if( !$sourceMapUrl && $sourceMapFilename ){ - $sourceMapUrl = $this->normalizeFilename($sourceMapFilename); - } - - // write map to a file - if( $sourceMapWriteTo ){ - $this->saveMap($sourceMapWriteTo, $sourceMapContent); - } - - // inline the map - if( !$sourceMapUrl ){ - $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent)); - } - - if( $sourceMapUrl ){ - $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) ); - } - - return $output->toString(); - } - - /** - * Saves the source map to a file - * - * @param string $file The absolute path to a file - * @param string $content The content to write - * @throws Exception If the file could not be saved - */ - protected function saveMap($file, $content){ - $dir = dirname($file); - // directory does not exist - if( !is_dir($dir) ){ - // FIXME: create the dir automatically? - throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir)); - } - // FIXME: proper saving, with dir write check! - if(file_put_contents($file, $content) === false){ - throw new Exception(sprintf('Cannot save the source map to "%s"', $file)); - } - return true; - } - - /** - * Normalizes the filename - * - * @param string $filename - * @return string - */ - protected function normalizeFilename($filename){ - - $filename = $this->fixWindowsPath($filename); - - $rootpath = $this->getOption('sourceMapRootpath'); - $basePath = $this->getOption('sourceMapBasepath'); - - // "Trim" the 'sourceMapBasepath' from the output filename. - if (strpos($filename, $basePath) === 0) { - $filename = substr($filename, strlen($basePath)); - } - - // Remove extra leading path separators. - if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){ - $filename = substr($filename, 1); - } - - return $rootpath . $filename; - } - - /** - * Adds a mapping - * - * @param integer $generatedLine The line number in generated file - * @param integer $generatedColumn The column number in generated file - * @param integer $originalLine The line number in original file - * @param integer $originalColumn The column number in original file - * @param string $sourceFile The original source file - */ - public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){ - - $this->mappings[] = array( - 'generated_line' => $generatedLine, - 'generated_column' => $generatedColumn, - 'original_line' => $originalLine, - 'original_column' => $originalColumn, - 'source_file' => $fileInfo['currentUri'] - ); - - $this->sources[$fileInfo['currentUri']] = $fileInfo['filename']; - } - - - /** - * Generates the JSON source map - * - * @return string - * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit# - */ - protected function generateJson(){ - - $sourceMap = array(); - $mappings = $this->generateMappings(); - - // File version (always the first entry in the object) and must be a positive integer. - $sourceMap['version'] = self::VERSION; - - - // An optional name of the generated code that this source map is associated with. - $file = $this->getOption('sourceMapFilename'); - if( $file ){ - $sourceMap['file'] = $file; - } - - - // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field. - $root = $this->getOption('sourceRoot'); - if( $root ){ - $sourceMap['sourceRoot'] = $root; - } - - - // A list of original sources used by the 'mappings' entry. - $sourceMap['sources'] = array(); - foreach($this->sources as $source_uri => $source_filename){ - $sourceMap['sources'][] = $this->normalizeFilename($source_filename); - } - - - // A list of symbol names used by the 'mappings' entry. - $sourceMap['names'] = array(); - - // A string with the encoded mapping data. - $sourceMap['mappings'] = $mappings; - - if( $this->getOption('outputSourceFiles') ){ - // An optional list of source content, useful when the 'source' can't be hosted. - // The contents are listed in the same order as the sources above. - // 'null' may be used if some original sources should be retrieved by name. - $sourceMap['sourcesContent'] = $this->getSourcesContent(); - } - - // less.js compat fixes - if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){ - unset($sourceMap['sourceRoot']); - } - - return json_encode($sourceMap); - } - - /** - * Returns the sources contents - * - * @return array|null - */ - protected function getSourcesContent(){ - if(empty($this->sources)){ - return; - } - $content = array(); - foreach($this->sources as $sourceFile){ - $content[] = file_get_contents($sourceFile); - } - return $content; - } - - /** - * Generates the mappings string - * - * @return string - */ - public function generateMappings(){ - - if( !count($this->mappings) ){ - return ''; - } - - $this->source_keys = array_flip(array_keys($this->sources)); - - - // group mappings by generated line number. - $groupedMap = $groupedMapEncoded = array(); - foreach($this->mappings as $m){ - $groupedMap[$m['generated_line']][] = $m; - } - ksort($groupedMap); - - $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0; - - foreach($groupedMap as $lineNumber => $line_map){ - while(++$lastGeneratedLine < $lineNumber){ - $groupedMapEncoded[] = ';'; - } - - $lineMapEncoded = array(); - $lastGeneratedColumn = 0; - - foreach($line_map as $m){ - $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn); - $lastGeneratedColumn = $m['generated_column']; - - // find the index - if( $m['source_file'] ){ - $index = $this->findFileIndex($m['source_file']); - if( $index !== false ){ - $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex); - $lastOriginalIndex = $index; - - // lines are stored 0-based in SourceMap spec version 3 - $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine); - $lastOriginalLine = $m['original_line'] - 1; - - $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn); - $lastOriginalColumn = $m['original_column']; - } - } - - $lineMapEncoded[] = $mapEncoded; - } - - $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';'; - } - - return rtrim(implode($groupedMapEncoded), ';'); - } - - /** - * Finds the index for the filename - * - * @param string $filename - * @return integer|false - */ - protected function findFileIndex($filename){ - return $this->source_keys[$filename]; - } - - /** - * fix windows paths - * @param string $path - * @return string - */ - public function fixWindowsPath($path, $addEndSlash = false){ - $slash = ($addEndSlash) ? '/' : ''; - if( !empty($path) ){ - $path = str_replace('\\', '/', $path); - $path = rtrim($path,'/') . $slash; - } - - return $path; - } - -} - \ No newline at end of file diff --git a/util/Line.class.php b/util/Line.class.php @@ -1,152 +0,0 @@ -<?php - -/** - * Darstellung einer Zeile in einem Freitext.<br> - * <br> - * Im Konstruktor wird die Zeile analysiert und es wird festgestellt, was - * die Zeile f�r einen Inhalt hat (z.B. ein Listenelement, eine �berschrift, usw.)<br> - * - * @author Jan Dankert - * @version $Revision$ - * @package openrat.services - */ -class Line -{ - var $source; // Der urspr�ngliche Inhalt - var $value; // Der textuelle Inhalt (sofern vorhanden) - - var $isDefinition = false; // Definitionseintrag - var $isList = false; // nicht-numerierte Liste - var $isNumberedList = false; // numerierte Liste - var $indent = 0; // Einschubtiefe der Liste - - var $isHeadline = false; // �berschrift - var $isHeadlineUnderline = false; // unterstrichene �berschrift - var $headlineLevel = 0; // Grad der �berschrift (1,2,3...) - - - var $isTableOfContent = false; // Inhaltsverzeichnis - var $isTable = false; // Tabelle - var $isCode = false; // Code - var $isQuote = false; // Zitat - var $isQuotePraefix = false; // Zitatbeginn/-ende - - var $isUnparsed = false; - - var $isEmpty = false; // Zeile ist leer - var $isNormal = false; // Zeile ist ohne besonderen Inhalt, d.h. keine Tabelle, kein Zitat, usw. - - - - /** - * Erzeugt einen Zeilenobjekt, der Text im Parameter wird dabei geparst. - */ - function __construct( $s ) - { - global $conf; - $text_markup = $conf['editor']['text-markup']; -// Html::debug($text_markup); - - $list_numbered = $text_markup['list-numbered' ]; - $list_unnumbered = $text_markup['list-unnumbered']; - $headline = $text_markup['headline' ]; - - $this->source = $s; - $this->value = $s; - - $this->isList = $this->isAnErsterStelle(ltrim($s),$list_unnumbered); - $this->isNumberedList = $this->isAnErsterStelle(ltrim($s),$list_numbered ); - $this->indent = strlen($s) - strlen(ltrim($s)) + 1; - - if ( $this->isList || $this->isNumberedList ) - $this->value = ltrim(substr($s,$this->indent)); - - $this->level = strspn( $s,$headline ); - $this->isHeadline = $this->level >= 1; - - if ( $this->isHeadline ) - $this->value = ltrim(substr($s,$this->level)); - - - $hl = array( 1 => $text_markup['headline_level1_underline'], - 2 => $text_markup['headline_level2_underline'], - 3 => $text_markup['headline_level3_underline'] ); - - foreach($hl as $lev=>$char ) - { - if ( substr($s,0,3*strlen($char))==str_repeat($char,3*strlen($char)) ) - { - $this->isHeadlineUnderline = true; - $this->isNormal = true; - $this->level = intval($lev); - } - } - - if ( $this->isAnErsterStelle($s,$text_markup['table-of-content']) ) - { - $this->isTableOfContent = true; - $this->isNumberedList = false; - $this->isList = false; - $this->value = ''; - } - elseif - ( $this->isHeadline || - $this->isHeadlineUnderline || - $this->isList || - $this->isNumberedList ) - { - ; - } - elseif ( $this->isAnErsterStelle($s,$text_markup['table-cell-sep']) ) - { - $this->isTable = true; - $this->value = ''; - } - elseif ( $this->isAnErsterStelle($s,$text_markup['pre-begin']) && !$this->isHeadlineUnderline ) - { - $this->isCode = true; - $this->value = substr($s,strlen($text_markup['pre-begin'])); - } - elseif ( strpos($s, $text_markup['definition-sep'])!== false ) - { - $this->isDefinition = true; - } - elseif ( trim($s)==$text_markup['quote-line-begin'] ) - { - $this->isQuote = true; - } - elseif ( $this->isAnErsterStelle($s,$text_markup['quote']) && strlen(trim($s))>1 ) - { - $this->isQuotePraefix = true; - $this->value = ltrim(substr($s,strlen($text_markup['quote']))); - $this->level = strspn( str_replace(' ','',$s),$text_markup['quote'] ); - } - elseif ( $this->isAnErsterStelle($s,'`') ) - { - $this->isUnparsed = true; - $this->value = substr($this->value,1); - } - elseif ( $s == '' ) - { - $this->isEmpty = true; - } - else - { - $this->isNormal = true; - } - } - - - /** - * Stellt fest, ob $wort am Anfang von $text steht. - */ - function isAnErsterStelle( $text,$wort ) - { -// Html::debug($text,"Text"); -// Html::debug($wort,"Wort"); -// Html::debug(substr($text,0,strlen($wort))==$wort,'Ergebnis'); - return substr($text,0,strlen($wort))==$wort; - } -} - -?>- \ No newline at end of file diff --git a/util/Macro.class.php b/util/Macro.class.php @@ -1,194 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -use cms\model\Page; - - -/** - * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere - * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen - * werden. - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Macro -{ - var $project; - var $output = ''; - var $objectid = 0; - - /** - * @var Page - */ - var $page; - var $parameters = array(); - var $description = ''; - - - /** - * Ausführen des Makros. Diese Methode sollte durch die Unterklasse überschrieben werden. - */ - public function execute() - { - // overwrite this in subclasses - } - - - /** - * Holt die aktuellen Datenbankverbindung. - */ - public function db() - { - return db_connection(); - } - - - /** - * Holt die aktuelle Objekt-Id. - * @return number - */ - public function getObjectId() - { - return $this->objectid; - } - - - /** - * Holt die aktuelle Seite. - * @return \cms\model\Page - */ - public function getPage() - { - return new Page( $this->objectid ); - } - - - /** - * Holt das aktuelle Objekt. - * @return Object - */ - public function &getObject() - { - return Session::getObject(); - } - - - /** - * Setzt eine Objekt-Id. - * @param int $objectid - */ - public function setObjectId( $objectid ) - { - $this->objectid = $objectid; - } - - - /** - * Ermittelt die Id des Wurzelordners im aktuellen Projekt. - */ - public function getRootObjectId() - { - $project = Session::getProject(); - return $project->getRootObjectId(); - } - - - /** - * DO NOT USE. - */ - public function folderid() - { - global $SESS; - return $SESS['folderid']; - } - - - /** - * Löscht die bisher erzeugte Ausgabe. - */ - public function delOutput() - { - $this->output = ''; - } - - /** - * Ergänzt die Standardausgabe um den gewünschten Wert. - */ - public function output( $text ) - { - $this->output .= $text; - } - - - /** - * Ergänzt die Standardausgabe um den gewünschten Wert. Es wird ein Zeilenendezeichen ergänzt. - */ - public function outputLn( $text ) - { - $this->output .= $text."\n"; - } - - - /** - * Ermittelt die bisher erstellte Ausgabe. - * @return string - */ - public function getOutput() - { - return $this->output; - } - - - /** - * Setzt eine Sitzungsvariable. - * - * @param String $var - * @param Object $value - */ - public function setSessionVar( $var,$value ) - { - Session::set( $var,$value ); - } - - - /** - * Ermittelt eine Sitzungsvariable. - * - * @param String $var - * @return string - */ - public function getSessionVar( $var ) - { - return Session::get( $var ); - } - - - /** - * Ermittelt den Pfad auf ein Objekt. - * @param Object - * @return string - */ - public function pathToObject( $obj ) - { - if ( is_object($obj) ) - return $this->page->path_to_object($obj->objectid); - else - return $this->page->path_to_object($obj); - } - -}- \ No newline at end of file diff --git a/util/Mail.class.php b/util/Mail.class.php @@ -1,571 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Erzeugen und Versender einer E-Mail gemaess RFC 822.<br> - * <br> - * Die E-Mail kann entweder �ber - * - die interne PHP-Funktion "mail()" versendet werden oder - * - direkt per SMTP-Protokoll an einen SMTP-Server.<br> - * Welcher Weg gew�hlt wird, kann konfiguriert werden.<br> - * <br> - * Prinzipiell spricht nichts gegen die interne PHP-Funktion mail(), wenn diese - * aber nicht zu Verf�gung steht oder PHP ungeeignet konfiguriert ist, so kann - * SMTP direkt verwendet werden. Hierbei sollte wenn m�glich ein Relay-Host - * eingesetzt werden. Die Mail kann zwar auch direkt an Mail-Exchanger (MX) des - * Empf�ngers geschickt werden, falls dieser aber Greylisting einsetzt ist eine - * Zustellung nicht m�glich.<br> - * <br> - * - * @author Jan Dankert - * @version $Id$ - * @package serviceClasses - */ -class Mail -{ - var $from = ''; - var $to = ''; - var $bcc = ''; - var $cc = ''; - var $subject = ''; - var $text = ''; - var $header = array(); - var $nl = ''; - - /** - * Falls beim Versendern der E-Mail etwas schiefgeht, steht hier drin - * die technische Fehlermeldung. - * - * @var String Fehler - */ - var $error = array(); - - /** - * Set to true for debugging. - * If true, All SMTP-Commands are written to error log. - * - * @var unknown_type - */ - var $debug = true; - - - /** - * Konstruktor. - * Es werden folgende Parameter erwartet - * @param String $to Empf�nger - * @param String der Textschl�ssel - * @param String unbenutzt. - * @return Mail - */ - function Mail( $to,$text,$xy='' ) - { - global $conf; - - // Zeilenumbruch CR/LF gem. RFC 822. - $this->nl = chr(13).chr(10); - - if ( !empty($conf['mail']['from']) ) - $this->from = $this->header_encode($conf['mail']['from']); - - // Priorit�t definieren (sofern konfiguriert) - if ( !empty($conf['mail']['priority']) ) - $this->header[] = 'X-Priority: '.$conf['mail']['priority']; - - $this->header[] = 'X-Mailer: '.$this->header_encode(OR_TITLE.' '.OR_VERSION); - $this->header[] = 'Content-Type: text/plain; charset='.lang( 'CHARSET' ); - $this->subject = $this->header_encode(lang( 'mail_subject_'.$text )); - $this->to = $this->header_encode($to); - - $this->text = $this->nl.wordwrap(str_replace(';',$this->nl,lang('mail_text_'.$text)),70,$this->nl).$this->nl; - - // Signatur anhaengen (sofern konfiguriert) - if ( !empty($conf['mail']['signature']) ) - { - $this->text .= $this->nl.'-- '.$this->nl; - $this->text .= str_replace(';',$this->nl,$conf['mail']['signature']); - $this->text .= $this->nl; - } - - // Kopie-Empf�nger - if ( !empty($conf['mail']['cc']) ) - $this->cc = $this->header_encode($conf['mail']['cc']); - - // Blindkopie-Empf�nger - if ( !empty($conf['mail']['bcc']) ) - $this->bcc = $this->header_encode($conf['mail']['bcc']); - } - - - - /** - * Kodiert einen Text in das Format "Quoted-printable".<br> - * See RFC 2045. - * @param String $text Eingabe - * @return Text im quoted-printable-Format - */ - function quoted_printable_encode( $text ) - { - $text = str_replace(' ','=20',$text); - - for( $i=128; $i<=255; $i++ ) - { - $text = str_replace( chr($i),'='.dechex($i),$text ); - } - - return $text; - } - - - - /** - * Setzen einer Variablen in den Mail-Inhalt. - */ - function setVar( $varName,$varInhalt) - { - $this->text = str_replace( '{'.$varName.'}', $varInhalt, $this->text ); - } - - - /** - * Mail absenden. - * Die E-Mail wird versendet. - * - * @return boolean Erfolg - */ - function send() - { - global $conf; - - $to_domain = array_pop( explode('@',$this->to) ); - - // Prüfen gegen die Whitelist - $white = @$conf['mail']['whitelist']; - - if ( !empty($white) ) - { - if ( ! $this->containsDomain($to_domain,$white) ) - { - // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken. - $this->error[] = 'Mail-Domain is not whitelisted'; - return false; - } - } - - // Prüfen gegen die Blacklist - $black = @$conf['mail']['blacklist']; - - if ( !empty($black) ) - { - if ( $this->containsDomain($to_domain,$black) ) - { - // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken. - $this->error[] = 'Mail-Domain is blacklisted'; - return false; - } - } - - // Header um Adressangaben erg�nzen. - if ( !empty($this->from ) ) - $this->header[] = 'From: '.$this->from; - - if ( !empty($this->cc ) ) - $this->header[] = 'Cc: '.$this->cc; - - if ( !empty($this->bcc ) ) - $this->header[] = 'Bcc: '.$this->bcc; - - // Mail versenden - if ( strtolower(@$conf['mail']['client']) == 'php' ) - { - // PHP-interne Mailfunktion verwenden. - $result = @mail( $this->to, // Empf�nger - $this->subject, // Betreff - $this->text, // Inhalt - // Lt. RFC822 müssen Header mit CRLF enden. - // ABER: Der Parameter "additional headers" verlangt offenbar \n am Zeilenende. - implode("\n",$this->header) ); - if ( !$result ) - // Die E-Mail wurde nicht akzeptiert. - // Genauer geht es leider nicht, da mail() nur einen boolean-Wert - // zur�ck liefert. - $this->error[] = 'Mail was NOT accepted by mail()'; - - return $result; - } - else - { - // eigenen SMTP-Dialog verwenden. - $smtpConf = $conf['mail']['smtp']; - - if ( !empty($smtpConf['host'])) - { - // Eigenen Relay-Host verwenden. - $mxHost = $smtpConf['host']; - $mxPort = intval($smtpConf['port']); - } - else - { - // Mail direkt zustellen. - $mxHost = $this->getMxHost($this->to); - - if ( empty($mxHost) ) - { - $this->error[] = "No MX-Entry found. Mail could not be sent."; - return false; - } - - if ($smtpConf['ssl']) - $mxPort = 465; - else - $mxPort = 25; - } - - - if ( !empty($smtpConf['localhost'])) - { - $myHost = $smtpConf['localhost']; - } - else - { - $myHost = gethostbyaddr(getenv('REMOTE_ADDR')); - } - - if ( $smtpConf['ssl']) - $proto = 'ssl'; - else - $proto = 'tcp'; - - //connect to the host and port - $smtpSocket = fsockopen($proto.'://'.$mxHost,$mxPort, $errno, $errstr, intval($smtpConf['timeout'])); - - if ( !is_resource($smtpSocket) ) - { - $this->error[] = 'Connection failed to: '.$proto.'://'.$mxHost.':'.$mxPort.' ('.$errstr.'/'.$errno.')'; - return false; - } - - $smtpResponse = fgets($smtpSocket, 4096); - if ( $this->debug) - $this->error[] = trim($smtpResponse); - - if ( substr($smtpResponse,0,3) != '220' ) - { - $this->error[] = 'No 220: '.trim($smtpResponse); - return false; - } - - if ( !is_resource($smtpSocket) ) - { - $this->error[] = 'Connection failed to: '.$smtpConf['host'].':'.$smtpConf['port'].' ('.$smtpResponse.')'; - return false; - } - - //you have to say HELO again after TLS is started - $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost); - - if ( substr($smtpResponse,0,3) != '250' ) - { - $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - if ( $smtpConf['tls'] ) - { - $smtpResponse = $this->sendSmtpCommand($smtpSocket,'STARTTLS'); - if ( substr($smtpResponse,0,3) == '220' ) - { - // STARTTLS ist gelungen. - //you have to say HELO again after TLS is started - $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost); - - if ( substr($smtpResponse,0,3) != '250' ) - { - $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - } - else - { - // STARTTLS ging in die Hose. Einfach weitermachen. - } - } - - // request for auth login - if ( isset($smtpConf['auth_username']) && !empty($smtpConf['host']) && !empty($smtpConf['auth_username'])) - { - $smtpResponse = $this->sendSmtpCommand($smtpSocket,"AUTH LOGIN"); - if ( substr($smtpResponse,0,3) != '334' ) - { - $this->error[] = "No 334 after AUTH_LOGIN, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - if ( $this->debug) - $this->error[] = 'Login for '.$smtpConf['auth_username']; - - //send the username - $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_username'])); - if ( substr($smtpResponse,0,3) != '334' ) - { - $this->error[] = "No 3xx after setting username, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - //send the password - $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_password'])); - if ( substr($smtpResponse,0,3) != '235' ) - { - $this->error[] = "No 235 after sending password, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - } - - //email from - $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'MAIL FROM: <'.$conf['mail']['from'].'>'); - if ( substr($smtpResponse,0,3) != '250' ) - { - $this->error[] = "No 2xx after MAIL_FROM, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - //email to - $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'RCPT TO: <'.$this->to.'>'); - if ( substr($smtpResponse,0,3) != '250' ) - { - $this->error[] = "No 2xx after RCPT_TO, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - //the email - $smtpResponse = $this->sendSmtpCommand($smtpSocket, "DATA"); - if ( substr($smtpResponse,0,3) != '354' ) - { - $this->error[] = "No 354 after DATA, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - $this->header[] = 'To: '.$this->to; - $this->header[] = 'Subject: '.$this->subject; - $this->header[] = 'Date: '.date('r'); - $this->header[] = 'Message-Id: '.'<'.getenv('REMOTE_ADDR').'.'.time().'.openrat@'.getenv('SERVER_NAME').'.'.getenv('HOSTNAME').'>'; - - //observe the . after the newline, it signals the end of message - $smtpResponse = $this->sendSmtpCommand($smtpSocket, implode($this->nl,$this->header).$this->nl.$this->nl.$this->text.$this->nl.'.'); - if ( substr($smtpResponse,0,3) != '250' ) - { - $this->error[] = "No 2xx after putting DATA, server says: ".$smtpResponse; - $this->sendSmtpQuit($smtpSocket); - return false; - } - - // say goodbye - $this->sendSmtpQuit($smtpSocket); - return true; - } - } - - - /** - * Sendet ein SMTP-Kommando zum SMTP-Server. - * - * @access private - * @param Resource $socket TCP/IP-Socket zum SMTP-Server - * @param unknown_type $cmd SMTP-Kommando - * @return Server-Antwort - */ - function sendSmtpCommand( $socket,$cmd ) - { - if ( $this->debug ) - $this->error[] = 'CLIENT: >>> '.trim($cmd); - if ( !is_resource($socket) ) - { - // Die Verbindung ist geschlossen. Dies kann bei dieser - // Implementierung eigentlich nur dann passieren, wenn - // der Server die Verbindung schlie�t. - // Dieser Client trennt die Verbindung nur nach einem "QUIT". - $this->error[] = "Connection lost"; - return; - } - - fputs($socket,$cmd.$this->nl); - $response = trim(fgets($socket, 4096)); - if ( $this->debug ) - $this->error[] = 'SERVER: <<< '.$response; - return $response; - } - - - - /** - * Sendet ein QUIT zum SMTP-Server, wartet die Antwort ab und - * schlie�t danach die Verbindung. - * - * @param Resource Socket - */ - function sendSmtpQuit( $socket ) - { - - if ( $this->debug ) - $this->error[] = "CLIENT: >>> QUIT"; - if ( !is_resource($socket) ) - return; - // Wenn die Verbindung nicht mehr da ist, brauchen wir - // auch kein QUIT mehr :) - - - fputs($socket,'QUIT'.$this->nl); - $response = trim(fgets($socket, 4096)); - if ( $this->debug ) - $this->error[] = 'SERVER: <<< '.$response; - - if ( substr($response,0,3) != '221' ) - $this->error[] = 'QUIT FAILED: '.$response; - - fclose($socket); - } - - - - /** - * Umwandlung von 8-bit-Zeichen in MIME-Header gemaess RFC 2047.<br> - * Header d�rfen nur 7-bit-Zeichen enthalten. 8-bit-Zeichen m�ssen kodiert werden. - * - * @param String $text - * @return String - */ - function header_encode( $text ) - { - global $conf; - - if ( empty($conf['mail']['header_encoding']) ) - return $text; - - $woerter = explode(' ',$text); - $neu = array(); - - - foreach( $woerter as $wort ) - { - $type = strtolower(substr($conf['mail']['header_encoding'],0,1)); - $neu_wort = ''; - - if ( $type == 'b' ) - $neu_wort = base64_encode($wort); - elseif ( $type == 'q' ) - $neu_wort = $this->quoted_printable_encode($wort); - else - Logger::error( 'Mail-Configuratin broken: UNKNOWN Header-Encoding type: '.$type); - - if ( strlen($wort)==strlen($neu_wort) ) - $neu[] = $wort; - else - $neu[] = '=?'.lang('CHARSET').'?'.$type.'?'.$neu_wort.'?='; - } - - return implode(' ',$neu); - } - - - /** - * Ermittelt den MX-Eintrag zu einer E-Mail-Adresse.<br> - * Es wird der Eintrag mit der h�chsten Priorit�t ermittelt. - * - * @param String E-Mail-Adresse des Empf�ngers. - * @return MX-Eintrag - */ - function getMxHost( $to ) - { - list($user,$host) = explode('@',$to.'@'); - - if ( empty($host) ) - { - $this->error[] = 'Illegal mail address - No hostname found.'; - return ""; - } - - list($host) = explode('>',$host); - - $mxHostsName = array(); - $mxHostsPrio = array(); - getmxrr($host,$mxHostsName,$mxHostsPrio); - - $mxList = array(); - foreach( $mxHostsName as $id=>$mxHostName ) - { - $mxList[$mxHostName] = $mxHostsPrio[$id]; - } - asort($mxList); - return key($mxList); - } - - - - /** - * Stellt fest, ob die E-Mail-Adresse eine gueltige Syntax besitzt. - * - * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange - * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen. - * - * @param $email_address Adresse - * @return true, falls Adresse OK, sonst false - */ - function checkAddress( $email_address ) - { - // Source: de.php.net/ereg - return ereg("^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$", $email_address); - } - - - - /** - * Prüft, ob eine Domain in einer List von Domains enthalten ist. - * - * @param $checkDomain zu prüfende Domain - * @param $domain_list Liste von Domains als kommaseparierte Liste - * @return true, falls vorhanden, sonst false - */ - function containsDomain($checkDomain, $domain_list) - { - $domains = explode(',',$domain_list); - - foreach( $domains as $domain ) - { - $domain = trim($domain); - - if (empty($domain)) - continue; - - if ($domain == substr($checkDomain,-strlen($domain))) - { - return true; - } - } - return false; - } -} - - -?> diff --git a/util/ProjectTree.class.php b/util/ProjectTree.class.php @@ -1,515 +0,0 @@ -<?php -use cms\model\Value; -use cms\model\Element; -use cms\model\Template; -use cms\model\Page; -use cms\model\Folder; -use cms\model\Object; -use cms\model\File; -use cms\model\Link; - -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Darstellen der Projektstruktur - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class ProjectTree extends AbstractTree -{ - var $projectId; - var $userIsProjectAdmin = false; - - function root() - { - $treeElement = new TreeElement(); - $treeElement->text = lang('GLOBAL_PROJECT'); - $treeElement->description = lang('GLOBAL_PROJECT'); - $treeElement->type = 'project'; - $treeElement->icon = 'project'; - - $this->addTreeElement( $treeElement ); - } - - - - function page( $id ) - { - $page = new Page( $id ); - $page->load(); - - $template = new Template( $page->templateid ); - - foreach( $template->getElementIds() as $elementid ) - { - $element = new Element( $elementid ); - $element->load(); - - if ( $element->isWritable() ) - { - $treeElement = new TreeElement(); - $treeElement->id = $id.'_'.$elementid; - $treeElement->extraId['elementid'] = $elementid; - $treeElement->text = $element->name; - $treeElement->url = Html::url('pageelement','edit', - $id.'_'.$elementid, - array('elementid'=>$elementid, - REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'pageelement'; - $treeElement->icon = 'el_'.$element->type; - - $treeElement->description = lang('EL_'.$element->type); - if ( $element->desc != '' ) - $treeElement->description .= ' - '.Text::maxLaenge( 25,$element->desc ); - else - $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - $treeElement->target = 'content'; - - if ( in_array($element->type,array('link','list','include') ) ) - { - $treeElement->type = 'value'; - $value = new Value(); - $value->pageid = $page->pageid; - $value->element = $element; - $value->load(); - $treeElement->internalId = $value->valueid; - } - - $this->addTreeElement( $treeElement ); - } - } - } - - - function value( $id ) - { - //echo "id: $id"; - if ( $id != 0 ) - { - $value = new Value(); - $value->loadWithId( $id ); - - $objectid = intval($value->linkToObjectId); - if ( $objectid != 0 ) - { - $object = new Object( $objectid ); - $object->load(); - - $treeElement = new TreeElement(); - $treeElement->id = $id; - $treeElement->text = $object->name; - if ( in_array($object->getType(),array('page','folder'))) - { - $treeElement->type = $object->getType(); - $treeElement->internalId = $object->objectid; - } - $treeElement->url = Html::url($object->getType(),'',$objectid,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = $object->getType(); - $treeElement->icon = $object->getType(); - - $treeElement->description = lang('GLOBAL_'.$object->getType()); - if ( $object->desc != '' ) - $treeElement->description .= ' - '.Text::maxLaenge( 25,$object->desc ); - else - $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - $treeElement->target = 'content'; - - $this->addTreeElement( $treeElement ); - } - } - } - - - function link( $id ) - { - $link = new Link( $id ); - $link->load(); - - if ( $link->isLinkToObject ) - { - $o = new Object( $link->linkedObjectId ); - $o->load(); - - $treeElement = new TreeElement(); - $treeElement->id = $o->objectid; - $treeElement->internalId = $o->objectid; - $treeElement->target = 'content'; - $treeElement->text = $o->name; - $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$id; - - if ( $o->desc != '' ) - $treeElement->description .= ': '.$o->desc; - else - $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - - $treeElement->url = Html::url($o->getType(),'',$o->objectid,array(REQ_PARAM_TARGET=>'content') ); - $treeElement->action = $o->getType(); - $treeElement->icon = $o->getType(); - - // Besonderheiten fuer bestimmte Objekttypen - - if ( $o->isPage ) - { - // Nur wenn die Seite beschreibbar ist, werden die - // Elemente im Baum angezeigt - if ( $o->hasRight( ACL_WRITE ) ) - $treeElement->type='pageelements'; - } - $this->addTreeElement( $treeElement ); - } - } - - - /** - * Laedt Elemente zu einem Ordner - * @return Array - */ - function folder( $id ) - { - global - $SESS, - $projectid; - - $f = new Folder( $id ); - $t = time(); - - foreach( $f->getObjects() as $o ) - { - // Wenn keine Leseberechtigung - if ( !$o->hasRight( ACL_READ ) ) - continue; - - $treeElement = new TreeElement(); - $treeElement->id = $o->objectid; - $treeElement->internalId = $o->objectid; - $treeElement->target = 'content'; - $treeElement->text = $o->name; - $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$o->objectid; - - if ( $o->desc != '' ) - $treeElement->description .= ': '.$o->desc; - else - $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - - $treeElement->url = Html::url( $o->getType(),'',$o->objectid,array('readit'=>'__OID__'.$o->objectid.'__',REQ_PARAM_TARGET=>'content') ); - $treeElement->action = $o->getType(); - $treeElement->icon = $o->getType(); - - // Besonderheiten fuer bestimmte Objekttypen - - if ( $o->isLink ) - { - $treeElement->type='link'; - } - - if ( $o->isPage ) - { - // Nur wenn die Seite beschreibbar ist, werden die - // Elemente im Baum angezeigt - if ( $o->hasRight( ACL_WRITE ) ) - $treeElement->type='page'; - } - - if ( $o->isFile ) - { - $file = new File( $o->objectid ); - $file->load(); - - if ( substr($file->mimeType(),0,6) == 'image/' ) - $treeElement->icon = 'image'; - else $treeElement->icon = 'file'; - } - - if ( $o->isFolder ) - { - $treeElement->type = 'folder'; - } - - - $this->addTreeElement( $treeElement ); - } - } - - - function project() - { - $language = Session::getProjectLanguage(); - $model = Session::getProjectModel(); - $user = Session::getUser(); - - $project = Session::getProject(); - $this->projectid = $project->projectid; - - // Hoechster Ordner der Projektstruktur - $folder = new Folder( $project->getRootObjectId() ); - $folder->load(); - - - // Ermitteln, ob der Benutzer Projektadministrator ist - // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern. - if ( $folder->hasRight( ACL_PROP ) ) - $this->userIsProjectAdmin = true; - - if ( $folder->hasRight( ACL_READ ) ) - { - $treeElement = new TreeElement(); - $treeElement->id = $folder->objectid; - // $treeElement->text = $folder->name; - $treeElement->text = lang('FOLDER_ROOT'); - $treeElement->description = lang('FOLDER_ROOT_DESC'); - $treeElement->icon = 'folder'; - $treeElement->action = 'folder'; - $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') ); - $treeElement->target = 'content'; - $treeElement->type = 'folder'; - $treeElement->internalId = $folder->objectid; - $this->addTreeElement( $treeElement ); - } - - - if ( $this->userIsProjectAdmin ) - { - // Templates - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_TEMPLATES'); - $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->description= lang('GLOBAL_TEMPLATES_DESC'); - $treeElement->icon = 'templatelist'; - $treeElement->action = 'templatelist'; - $treeElement->target = 'content'; - $treeElement->type = 'templates'; - $this->addTreeElement( $treeElement ); - } - - - // Sprachen - $treeElement = new TreeElement(); - $treeElement->description= ''; - $treeElement->id = 0; - $treeElement->action = 'languagelist'; - $treeElement->text = lang('GLOBAL_LANGUAGES'); - $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'languagelist'; - $treeElement->description= lang('GLOBAL_LANGUAGES_DESC'); - $treeElement->target = 'content'; - - // Nur fuer Projekt-Administratoren aufklappbar - if ( $this->userIsProjectAdmin ) - $treeElement->type = 'languages'; - - $this->addTreeElement( $treeElement ); - - - // Projektmodelle - $treeElement = new TreeElement(); - $treeElement->description= ''; - - // Nur fuer Projekt-Administratoren aufklappbar - if ( $this->userIsProjectAdmin ) - $treeElement->type = 'models'; - - $treeElement->id = 0; - $treeElement->description= lang('GLOBAL_MODELS_DESC'); - $treeElement->text = lang('GLOBAL_MODELS'); - $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'modellist'; - $treeElement->icon = 'modellist'; - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - - // Sonstiges -// $treeElement = new TreeElement(); -// $treeElement->text = lang('GLOBAL_OTHER'); -// $treeElement->description= lang('GLOBAL_OTHER_DESC'); -// $treeElement->icon = 'other'; -// $treeElement->type = 'other'; -// $this->addTreeElement( $treeElement ); - - // Suche - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_SEARCH'); - $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'search'; - $treeElement->icon = 'search'; - $treeElement->description = lang('GLOBAL_SEARCH_DESC'); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - } - - - function templates() - { - foreach( Template::getAll() as $id=>$name ) - { - $treeElement = new TreeElement(); - - $t = new Template( $id ); - $t->load(); - $treeElement->text = $t->name; - $treeElement->id = $id; - $treeElement->url = Html::url('template','src',$id,array(REQ_PARAM_TARGETSUBACTION=>'src',REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'template'; - $treeElement->action = 'template'; - $treeElement->target = 'content'; - $treeElement->internalId = $id; - $treeElement->type = 'template'; - $treeElement->description = $t->name.' ('.lang('GLOBAL_TEMPLATE').' '.$id.'): '.htmlentities(Text::maxLaenge( 40,$t->src )); - $this->addTreeElement( $treeElement ); - } - } - - - function template( $id ) - { - - $t = new Template( $id ); - $t->load(); - - // Anzeigen der Template-Elemente - // - foreach( $t->getElementIds() as $elementid ) - { - $e = new Element( $elementid ); - $e->load(); - - // "Code"-Element nur fuer Administratoren - if ( $e->type == 'code' && !$this->userIsAdmin ) - continue; - - $treeElement = new TreeElement(); - $treeElement->id = $elementid; - $treeElement->text = $e->name; - $treeElement->url = Html::url('element','',$elementid,array(REQ_PARAM_TARGET=>'content') ); - $treeElement->icon = 'el_'.$e->type; - $treeElement->action = 'element'; - - if ( $e->desc == '' ) - $desc = lang('GLOBAL_NO_DESCRIPTION_AVAILABLE'); - else - $desc = $e->desc; - $treeElement->description = $e->name.' ('.lang('EL_'.$e->type).'): '.Text::maxLaenge( 40,$desc ); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - } - } - - - /** - * Sprachen - */ - function languages() - { - // Sprachvarianten - // - $l = Session::getProjectLanguage(); - $languages = $l->getAll(); - - foreach( $languages as $languageid=>$name ) - { - $treeElement = new TreeElement(); - $treeElement->id = $languageid; - $treeElement->text = $name; - $treeElement->url = Html::url('language','edit',$languageid, - array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content') ); - $treeElement->icon = 'language'; - $treeElement->action = 'language'; - $treeElement->description = ''; - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - } - } - - - // Projektvarianten - // - function models() - { - $m = Session::getProjectModel(); - $models = $m->getAll(); - - foreach( $models as $id=>$name ) - { - $treeElement = new TreeElement(); - $treeElement->id = $id; - $treeElement->text = $name; - $treeElement->url = Html::url('model','edit',$id, - array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content')); - $treeElement->action = 'model'; - $treeElement->icon = 'model'; - $treeElement->description = ''; - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - } - } - - - function other() - { -// Deaktiviert, da -// - Dateien auf den Server laden unverst�ndlich/undurchsichtig erscheint -// - M�glichkeit zum Entpacken von ZIP/TAR online besteht. -// if ( $this->userIsProjectAdmin ) -// { -// $treeElement = new TreeElement(); -// $treeElement->text = lang('GLOBAL_FILE_TRANSFER'); -// $treeElement->description = lang('GLOBAL_FILE_TRANSFER_DESC'); -// $treeElement->url = Html::url('main','transfer'); -// $treeElement->icon = 'transfer'; -// $treeElement->target = 'content'; -// $this->addTreeElement( $treeElement ); -// } - - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_SEARCH'); - $treeElement->url = Html::url('search'); - $treeElement->icon = 'search'; - $treeElement->action = 'search'; - $treeElement->description = lang('GLOBAL_SEARCH_DESC'); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('USER_YOURPROFILE'); - $treeElement->url = Html::url('profile','edit',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'user'; - $treeElement->action = 'profile'; - $treeElement->description = lang('USER_PROFILE_DESC'); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - - - $treeElement = new TreeElement(); - $treeElement->id = 0; - $treeElement->text = lang('GLOBAL_PROJECTS'); - $treeElement->url = Html::url('index','projectmenu',0,array(REQ_PARAM_TARGET=>'content')); - $treeElement->icon = 'project'; - $treeElement->description = lang('GLOBAL_PROJECTS'); - $treeElement->target = 'content'; - $this->addTreeElement( $treeElement ); - } -} - -?>- \ No newline at end of file diff --git a/util/Publish.class.php b/util/Publish.class.php @@ -1,393 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Diese Klasse kapselt das Veroeffentlichen von Dateien.<br> - * <br> - * Hier werden<br> - * - Dateien in das Zielverzeichnis kopiert<br> - * - Dateien per FTP veroeffentlicht<br> - * - Zielverzeichnisse aufgeraeumt<br> - * - Systembefehle ausgefuehrt. - * - * @author Jan Dankert - * @package openrat.services - */ -class Publish -{ - /** - * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn - * zu einem FTP-Server veroeffentlicht werden soll. - * @var Object - */ - var $ftp; - - /** - * Flag, ob in das lokale Dateisystem veroeffentlicht werden soll. - * @var boolean - */ - var $with_local = false; - - /** - * Flag, ob zu einem FTP-Server ver�ffentlicht werden soll. - * @var boolean - */ - var $with_ftp = false; - - var $local_destdir = ''; - - /** - * Enthaelt die gleichnamige Einstellung aus dem Projekt. - * @var boolean - */ - var $content_negotiation = false; - - /** - * Enthaelt die gleichnamige Einstellung aus dem Projekt. - * @var boolean - */ - var $cut_index = false; - - /** - * Enthaelt die gleichnamige Einstellung aus dem Projekt. - * @var String - */ - var $cmd_after_publish = ''; - - /** - * Enthaelt am Ende der Ver�ffentlichung ein Array mit den ver�ffentlichten Objekten. - * @var Array - */ - var $publishedObjects = array(); - - /** - * Enthaelt im Fehlerfall (wenn 'ok' auf 'false' steht) eine - * Fehlermeldung. - * - * @var String - */ - var $log = array(); - - /** - * Stellt nach der Ver�ffentlichung fest, ob der Vorgang erfolgreich ist. - * Falls nicht, enth�lt die Variable 'log' eine Fehlermeldung. - * @var boolean - */ - var $ok = true; - - /** - * Konstruktor.<br> - * <br> - * Oeffnet ggf. Verbindungen. - * - * @return Publish - */ - function __construct() - { - global $conf; - $confPublish = $conf['publish']; - - if ( $conf['security']['nopublish'] ) - { - $this->ok = false; - $this->log[] = 'publishing is disabled.'; - return; - } - - $project = Session::getProject(); - - // Feststellen, ob FTP benutzt wird. - // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein. - $ftpUrl = ''; - if ( $conf['publish']['ftp']['enable'] ) - { - if ( $conf['publish']['ftp']['per_project'] && !empty($project->ftp_url) ) - $ftpUrl = $project->ftp_url; - elseif ( !empty($conf['publish']['ftp']['host']) ) - $ftpUrl = $project->ftp_url; - } - - if ( ! empty($ftpUrl) ) - { - $this->with_ftp = true; - $this->ftp = new Ftp( $project->ftp_url ); // Aufbauen einer FTP-Verbindung - - if ( ! $this->ftp->ok ) // FTP-Verbindung ok? - { - $this->ok = false; - $this->log = $this->ftp->log; - return; // Ende. Ohne FTP brauchen wir nicht weitermachen. - } - - $this->ftp->passive = ( $project->ftp_passive == '1' ); - } - - $localDir = rtrim( $project->target_dir,'/' ); - - if ( $confPublish['filesystem']['per_project'] && (!empty($localDir)) ) - { - $this->local_destdir = $localDir; // Projekteinstellung verwenden. - } - else - { - if ( empty( $localDir)) - $localDir = $project->name; - // Konfiguriertes Verzeichnis verwenden. - $this->local_destdir = $confPublish['filesystem']['directory'].$localDir; - } - - - // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist. - if ( $this->local_destdir != '' ) - { - if ( !is_writeable( $this->local_destdir ) ) - { - $this->ok = false; - $this->log[] = 'directory not writable: '.$this->local_destdir; - $this->log[] = 'please correct the file permissions.'; - return; - } - - $this->with_local = true; - } - - $this->content_negotiation = ( $project->content_negotiation == '1' ); - $this->cut_index = ( $project->cut_index == '1' ); - - if ( $confPublish['command']['enable'] ) - { - if ( $confPublish['command']['per_project'] && !empty($project->cmd_after_publish) ) - $this->cmd_after_publish = $project->cmd_after_publish; - else - $this->cmd_after_publish = @$confPublish['command']['command']; - } - - // Im Systemkommando Variablen ersetzen - $this->cmd_after_publish = str_replace('{name}' ,$project->name ,$this->cmd_after_publish); - $this->cmd_after_publish = str_replace('{dir}' ,$this->local_destdir ,$this->cmd_after_publish); - $this->cmd_after_publish = str_replace('{dirbase}',basename($this->local_destdir),$this->cmd_after_publish); - } - - - - /** - * Kopieren einer Datei aus dem tempor�ren Verzeichnis in das Zielverzeichnis.<br> - * Falls notwenig, wird ein Hochladen per FTP ausgef�hrt. - * - * @param String $tmp_filename - * @param String $dest_filename - */ - function copy( $tmp_filename,$dest_filename,$lastChangeDate=null ) - { - if ( !$this->ok) - return; - - global $conf; - $source = $tmp_filename; - - if ( $this->with_local ) - { - $dest = $this->local_destdir.'/'.$dest_filename; - - if (!@copy( $source,$dest )); - { - if ( ! $this->mkdirs( dirname($dest) ) ) - return; // Fehler bei Verzeichniserstellung, also abbrechen. - - if (!@copy( $source,$dest )) - { - $this->ok = false; - $this->log[] = 'failed copying local file:'; - $this->log[] = 'source : '.$source; - $this->log[] = 'destination: '.$dest; - return; // Fehler beim Kopieren, also abbrechen. - } - if ( ! is_null($lastChangeDate) ) - @touch( $dest,$lastChangeDate ); - - Logger::debug("published: $dest"); - } - - if (!empty($conf['security']['chmod'])) - { - // CHMOD auf der Datei ausfuehren. - if ( ! @chmod($dest,octdec($conf['security']['chmod'])) ) - { - $this->ok = false; - $this->log[] = 'Unable to CHMOD file '.$dest; - return; - } - } - } - - if ( $this->with_ftp ) // Falls FTP aktiviert - { - $dest = $dest_filename; - $this->ftp->put( $source,$dest ); - - if ( ! $this->ftp->ok ) - { - $this->ok = false; - $this->log[] = $this->ftp->log; - } - } - } - - - - /** - * Rekursives Anlagen von Verzeichnisse - * Nett gemacht. - * Quelle: http://de3.php.net/manual/de/function.mkdir.php - * Thx to acroyear at io dot com - * - * @param String Verzeichnis - * @return boolean - */ - function mkdirs( $strPath ) - { - global $conf; - - if ( is_dir($strPath) ) - return true; - - $pStrPath = dirname($strPath); - if ( !$this->mkdirs($pStrPath) ) - return false; - - if ( ! @mkdir($strPath,0777) ) - { - $this->ok = false; - $this->log[] = 'Cannot create directory: '.$strPath; - return false; - } - - // CHMOD auf dem Verzeichnis ausgef�hren. - if (!empty($conf['security']['chmod_dir'])) - { - if ( ! @chmod($strPath,octdec($conf['security']['chmod_dir'])) ) - { - $this->ok = false; - $this->log[] = 'Unable to CHMOD directory: '.$strPath; - return false; - } - } - - - return $this->ok; - } - - - - /** - * Beenden des Ver�ffentlichungs-Vorganges.<br> - * Eine vorhandene FTP-Verbindung wird geschlossen.<br> - * Falls entsprechend konfiguriert, wird ein Systemkommando ausgef�hrt. - */ - public function close() - { - if ( $this->with_ftp ) - { - Logger::debug('Closing FTP connection' ); - $this->ftp->close(); - } - - // Ausfuehren des Systemkommandos. - if ( !empty($this->cmd_after_publish) && $this->ok ) - { - $ausgabe = array(); - $rc = false; - Logger::debug('Executing system command: '.$this->cmd_after_publish ); - $user = Session::getUser(); - putenv("CMS_USER_NAME=".$user->name ); - putenv("CMS_USER_ID=" .$user->userid); - putenv("CMS_USER_MAIL=".$user->mail ); - exec( $this->cmd_after_publish,$ausgabe,$rc ); - - if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Ausgabe ins Log schreiben und Fehler melden. - { - $this->log = $ausgabe; - $this->log[] = 'OpenRat: System command failed - returncode is '.$rc; - $this->ok = false; - - Logger::warn('System command '.$this->cmd_after_publish.' failed with status '.$rc ); - - } - else - { - Logger::debug('System command successful' ); - } - - } - } - - - - /** - * Aufraeumen des Zielverzeichnisses.<br><br> - * Es wird der komplette Zielordner samt Unterverzeichnissen durchsucht. Jede - * Datei, die laenger existiert als der aktuelle Request alt ist, wird geloescht.<br> - * Natuerlich darf diese Funktion nur nach einem Gesamt-Veroeffentlichen ausgefuehrt werden. - */ - function clean() - { - if ( $this->ok ) - return; - - if ( !empty($this->local_destdir) ) - $this->cleanFolder($this->local_destdir); - } - - - - /** - * Aufr�umen eines Verzeichnisses.<br><br> - * Dateien, die l�nger existieren als der aktuelle Request alt ist, werden gel�scht.<br> - * - * @param String Verzeichnis - */ - function cleanFolder( $folderName ) - { - $dh = opendir( $folderName ); - - while( $file = readdir($dh) ) - { - if ( $file != '.' && $file != '..') - { - $fullpath = $folderName.'/'.$file; - - // Wenn eine Datei beschreibbar und entsprechend alt - // ist, dann entfernen - if ( is_file($fullpath) && - is_writable($fullpath) && - filemtime($fullpath) < START_TIME ) - unlink($fullpath); - - // Bei Ordnern rekursiv absteigen - if ( is_dir( $fullpath) ) - { - $this->cleanFolder($fullpath); - @rmdir($fullpath); - } - } - } - } - -} - -?>- \ No newline at end of file diff --git a/util/Session.class.php b/util/Session.class.php @@ -1,242 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -// PHP-Versionsunabhaengiges Array fuer die Session-Variablen ermitteln -use cms\model\User; - -if (isset($_SESSION)) - $SESS = &$_SESSION; -else $SESS = &$HTTP_SESSION_VARS; - -if ( isset($_FILES) ) - $FILES = &$_FILES; -else $FILES = &$HTTP_POST_FILES; - - -/** - * Session-Funktionen zum Lesen/Schreiben in/von HTTP-Session - * In der Session werden folgende Daten abgelegt - * - Ausgewaehltes Projekt - * - Ausgewaehlte Projectsprache - * - Ausgewaehlte Projektvariante - * - Angemeldeter Benutzer - * - Auswahlbaum - * - Geladene Sprachelemente - * - Ausgewaehlter Ordner - * - Ausgewaehltes Objekt - * - Datenbankobjekt - * Die Methoden dieser Klassen koennen statisch aufgerufen werden - * - * @author $Author$ - * @version $Revision$ - * @package openrat.service - */ - -class Session -{ - public static function get( $var ) - { - global $SESS; - if ( isset($SESS['ors_'.$var]) ) - return $SESS['ors_'.$var]; - else - return ''; - } - - public static function set( $var,$value ) - { - global $SESS; - $SESS[ 'ors_'.$var ] = $value; - } - - - /** - * @return array - */ - public static function getConfig() - { - return Session::get('config'); - } - - public static function setConfig( $var ) - { - Session::set('config',$var); - } - - - /** - * @return \cms\model\Model - */ - public static function getProjectModel() - { - return Session::get('project_model'); - } - - public static function setProjectModel( $var ) - { - Session::set('project_model',$var); - } - - - /** - * @return \cms\model\Language - */ - public static function getProjectLanguage() - { - return Session::get('project_language'); - } - - public static function setProjectLanguage( $var ) - { - Session::set('project_language',$var); - } - - - - public static function getObject() - { - return Session::get('object'); - } - - public static function setObject( $var ) - { - Session::set('object',$var); - } - - - /** - * @return \cms\model\Folder - */ - public static function getFolder() - { - return Session::get('folder'); - } - - public static function setFolder( $var ) - { - Session::set('folder',$var); - } - - - - public static function getTree() - { - return Session::get('tree'); - } - - public static function setTree( $var ) - { - Session::set('tree',$var); - } - - - - public static function getElement() - { - return Session::get('element'); - } - - public static function setElement( $var ) - { - Session::set('element',$var); - } - - - /** - * @return \cms\model\Project - */ - public static function getProject() - { - return Session::get('project'); - } - - public static function setProject( $var ) - { - Session::set('project',$var); - } - - - /** - * @return User - */ - public static function getUser() - { - return Session::get('userObject'); - } - - public static function setUser( $var ) - { - Session::set('userObject',$var); - } - - - /** - * @return \database\Database - */ - public static function getDatabase() - { - return Session::get('database'); - } - - public static function setDatabase( $var ) - { - Session::set('database',$var); - } - - - /** - * @return string - */ - public static function getSubaction() - { - return Session::get('subaction'); - } - - public static function setSubaction( $var ) - { - Session::set('subaction',$var); - } - - - public static function getClipboard() - { - return Session::get('clipboard'); - } - - public static function setClipboard( $var ) - { - Session::set('clipboard',$var); - } - - - /** - * Schliesst die aktuelle Session - * - * Diese Funktion sollte so schnell wie moeglich aufgerufen werden, da vorher - * keine andere Seite (im Frameset!) geladen werden kann - * Nach Aufruf dieser Methode sind keine Session-Zugriffe ueber diese Klasse mehr - * moeglich. - */ - public static function close() - { - session_write_close(); - } -} - -?>- \ No newline at end of file diff --git a/util/Spyc.class.php b/util/Spyc.class.php @@ -1,1161 +0,0 @@ -<?php -/** - * Spyc -- A Simple PHP YAML Class - * @version 0.6.2 - * @author Vlad Andersen <vlad.andersen@gmail.com> - * @author Chris Wanstrath <chris@ozmm.org> - * @link https://github.com/mustangostang/spyc/ - * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen - * @license http://www.opensource.org/licenses/mit-license.php MIT License - * @package Spyc - */ - -if (!function_exists('spyc_load')) { - /** - * Parses YAML to array. - * @param string $string YAML string. - * @return array - */ - function spyc_load ($string) { - return Spyc::YAMLLoadString($string); - } -} - -if (!function_exists('spyc_load_file')) { - /** - * Parses YAML to array. - * @param string $file Path to YAML file. - * @return array - */ - function spyc_load_file ($file) { - return Spyc::YAMLLoad($file); - } -} - -if (!function_exists('spyc_dump')) { - /** - * Dumps array to YAML. - * @param array $data Array. - * @return string - */ - function spyc_dump ($data) { - return Spyc::YAMLDump($data, false, false, true); - } -} - -if (!class_exists('Spyc')) { - -/** - * The Simple PHP YAML Class. - * - * This class can be used to read a YAML file and convert its contents - * into a PHP array. It currently supports a very limited subsection of - * the YAML spec. - * - * Usage: - * <code> - * $Spyc = new Spyc; - * $array = $Spyc->load($file); - * </code> - * or: - * <code> - * $array = Spyc::YAMLLoad($file); - * </code> - * or: - * <code> - * $array = spyc_load_file($file); - * </code> - * @package Spyc - */ -class Spyc { - - // SETTINGS - - const REMPTY = "\0\0\0\0\0"; - - /** - * Setting this to true will force YAMLDump to enclose any string value in - * quotes. False by default. - * - * @var bool - */ - public $setting_dump_force_quotes = false; - - /** - * Setting this to true will forse YAMLLoad to use syck_load function when - * possible. False by default. - * @var bool - */ - public $setting_use_syck_is_possible = false; - - - - /**#@+ - * @access private - * @var mixed - */ - private $_dumpIndent; - private $_dumpWordWrap; - private $_containsGroupAnchor = false; - private $_containsGroupAlias = false; - private $path; - private $result; - private $LiteralPlaceHolder = '___YAML_Literal_Block___'; - private $SavedGroups = array(); - private $indent; - /** - * Path modifier that should be applied after adding current element. - * @var array - */ - private $delayedPath = array(); - - /**#@+ - * @access public - * @var mixed - */ - public $_nodeId; - -/** - * Load a valid YAML string to Spyc. - * @param string $input - * @return array - */ - public function load ($input) { - return $this->_loadString($input); - } - - /** - * Load a valid YAML file to Spyc. - * @param string $file - * @return array - */ - public function loadFile ($file) { - return $this->_load($file); - } - - /** - * Load YAML into a PHP array statically - * - * The load method, when supplied with a YAML stream (string or file), - * will do its best to convert YAML in a file into a PHP array. Pretty - * simple. - * Usage: - * <code> - * $array = Spyc::YAMLLoad('lucky.yaml'); - * print_r($array); - * </code> - * @access public - * @return array - * @param string $input Path of YAML file or string containing YAML - */ - public static function YAMLLoad($input) { - $Spyc = new Spyc; - return $Spyc->_load($input); - } - - /** - * Load a string of YAML into a PHP array statically - * - * The load method, when supplied with a YAML string, will do its best - * to convert YAML in a string into a PHP array. Pretty simple. - * - * Note: use this function if you don't want files from the file system - * loaded and processed as YAML. This is of interest to people concerned - * about security whose input is from a string. - * - * Usage: - * <code> - * $array = Spyc::YAMLLoadString("---\n0: hello world\n"); - * print_r($array); - * </code> - * @access public - * @return array - * @param string $input String containing YAML - */ - public static function YAMLLoadString($input) { - $Spyc = new Spyc; - return $Spyc->_loadString($input); - } - - /** - * Dump YAML from PHP array statically - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as nothing.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array|\stdClass $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - * @param bool $no_opening_dashes Do not start YAML file with "---\n" - */ - public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) { - $spyc = new Spyc; - return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes); - } - - - /** - * Dump PHP array to YAML - * - * The dump method, when supplied with an array, will do its best - * to convert the array into friendly YAML. Pretty simple. Feel free to - * save the returned string as tasteful.yaml and pass it around. - * - * Oh, and you can decide how big the indent is and what the wordwrap - * for folding is. Pretty cool -- just pass in 'false' for either if - * you want to use the default. - * - * Indent's default is 2 spaces, wordwrap's default is 40 characters. And - * you can turn off wordwrap by passing in 0. - * - * @access public - * @return string - * @param array $array PHP array - * @param int $indent Pass in false to use the default, which is 2 - * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) - */ - public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) { - // Dumps to some very clean YAML. We'll have to add some more features - // and options soon. And better support for folding. - - // New features and options. - if ($indent === false or !is_numeric($indent)) { - $this->_dumpIndent = 2; - } else { - $this->_dumpIndent = $indent; - } - - if ($wordwrap === false or !is_numeric($wordwrap)) { - $this->_dumpWordWrap = 40; - } else { - $this->_dumpWordWrap = $wordwrap; - } - - // New YAML document - $string = ""; - if (!$no_opening_dashes) $string = "---\n"; - - // Start at the base of the array and move through it. - if ($array) { - $array = (array)$array; - $previous_key = -1; - foreach ($array as $key => $value) { - if (!isset($first_key)) $first_key = $key; - $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array); - $previous_key = $key; - } - } - return $string; - } - - /** - * Attempts to convert a key / value array item to YAML - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) { - if(is_object($value)) $value = (array)$value; - if (is_array($value)) { - if (empty ($value)) - return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array); - // It has children. What to do? - // Make it the right kind of item - $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array); - // Add the indent - $indent += $this->_dumpIndent; - // Yamlize the array - $string .= $this->_yamlizeArray($value,$indent); - } elseif (!is_array($value)) { - // It doesn't have children. Yip. - $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array); - } - return $string; - } - - /** - * Attempts to convert an array to YAML - * @access private - * @return string - * @param $array The array you want to convert - * @param $indent The indent of the current level - */ - private function _yamlizeArray($array,$indent) { - if (is_array($array)) { - $string = ''; - $previous_key = -1; - foreach ($array as $key => $value) { - if (!isset($first_key)) $first_key = $key; - $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array); - $previous_key = $key; - } - return $string; - } else { - return false; - } - } - - /** - * Returns YAML from a key and a value - * @access private - * @return string - * @param $key The name of the key - * @param $value The value of the item - * @param $indent The indent of the current node - */ - private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) { - // do some folding here, for blocks - if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false || - strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false || - strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 || - substr ($value, -1, 1) == ':') - ) { - $value = $this->_doLiteralBlock($value,$indent); - } else { - $value = $this->_doFolding($value,$indent); - } - - if ($value === array()) $value = '[ ]'; - if ($value === "") $value = '""'; - if (self::isTranslationWord($value)) { - $value = $this->_doLiteralBlock($value, $indent); - } - if (trim ($value) != $value) - $value = $this->_doLiteralBlock($value,$indent); - - if (is_bool($value)) { - $value = $value ? "true" : "false"; - } - - if ($value === null) $value = 'null'; - if ($value === "'" . self::REMPTY . "'") $value = null; - - $spaces = str_repeat(' ',$indent); - - //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) { - if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) { - // It's a sequence - $string = $spaces.'- '.$value."\n"; - } else { - // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"'); - // It's mapped - if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; } - $string = rtrim ($spaces.$key.': '.$value)."\n"; - } - return $string; - } - - /** - * Creates a literal block for dumping - * @access private - * @return string - * @param $value - * @param $indent int The value of the indent - */ - private function _doLiteralBlock($value,$indent) { - if ($value === "\n") return '\n'; - if (strpos($value, "\n") === false && strpos($value, "'") === false) { - return sprintf ("'%s'", $value); - } - if (strpos($value, "\n") === false && strpos($value, '"') === false) { - return sprintf ('"%s"', $value); - } - $exploded = explode("\n",$value); - $newValue = '|'; - if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) { - $newValue = $exploded[0]; - unset($exploded[0]); - } - $indent += $this->_dumpIndent; - $spaces = str_repeat(' ',$indent); - foreach ($exploded as $line) { - $line = trim($line); - if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) { - $line = substr($line, 1, -1); - } - $newValue .= "\n" . $spaces . ($line); - } - return $newValue; - } - - /** - * Folds a string of text, if necessary - * @access private - * @return string - * @param $value The string you wish to fold - */ - private function _doFolding($value,$indent) { - // Don't do anything if wordwrap is set to 0 - - if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) { - $indent += $this->_dumpIndent; - $indent = str_repeat(' ',$indent); - $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); - $value = ">\n".$indent.$wrapped; - } else { - if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY) - $value = '"' . $value . '"'; - if (is_numeric($value) && is_string($value)) - $value = '"' . $value . '"'; - } - - - return $value; - } - - private function isTrueWord($value) { - $words = self::getTranslations(array('true', 'on', 'yes', 'y')); - return in_array($value, $words, true); - } - - private function isFalseWord($value) { - $words = self::getTranslations(array('false', 'off', 'no', 'n')); - return in_array($value, $words, true); - } - - private function isNullWord($value) { - $words = self::getTranslations(array('null', '~')); - return in_array($value, $words, true); - } - - private function isTranslationWord($value) { - return ( - self::isTrueWord($value) || - self::isFalseWord($value) || - self::isNullWord($value) - ); - } - - /** - * Coerce a string into a native type - * Reference: http://yaml.org/type/bool.html - * TODO: Use only words from the YAML spec. - * @access private - * @param $value The value to coerce - */ - private function coerceValue(&$value) { - if (self::isTrueWord($value)) { - $value = true; - } else if (self::isFalseWord($value)) { - $value = false; - } else if (self::isNullWord($value)) { - $value = null; - } - } - - /** - * Given a set of words, perform the appropriate translations on them to - * match the YAML 1.1 specification for type coercing. - * @param $words The words to translate - * @access private - */ - private static function getTranslations(array $words) { - $result = array(); - foreach ($words as $i) { - $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i))); - } - return $result; - } - -// LOADING FUNCTIONS - - private function _load($input) { - $Source = $this->loadFromSource($input); - return $this->loadWithSource($Source); - } - - private function _loadString($input) { - $Source = $this->loadFromString($input); - return $this->loadWithSource($Source); - } - - private function loadWithSource($Source) { - if (empty ($Source)) return array(); - if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) { - $array = syck_load (implode ("\n", $Source)); - return is_array($array) ? $array : array(); - } - - $this->path = array(); - $this->result = array(); - - $cnt = count($Source); - for ($i = 0; $i < $cnt; $i++) { - $line = $Source[$i]; - - $this->indent = strlen($line) - strlen(ltrim($line)); - $tempPath = $this->getParentPathByIndent($this->indent); - $line = self::stripIndent($line, $this->indent); - if (self::isComment($line)) continue; - if (self::isEmpty($line)) continue; - $this->path = $tempPath; - - $literalBlockStyle = self::startsLiteralBlock($line); - if ($literalBlockStyle) { - $line = rtrim ($line, $literalBlockStyle . " \n"); - $literalBlock = ''; - $line .= ' '.$this->LiteralPlaceHolder; - $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1])); - while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) { - $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent); - } - $i--; - } - - // Strip out comments - if (strpos ($line, '#')) { - $line = preg_replace('/\s*#([^"\']+)$/','',$line); - } - - while (++$i < $cnt && self::greedilyNeedNextLine($line)) { - $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t"); - } - $i--; - - $lineArray = $this->_parseLine($line); - - if ($literalBlockStyle) - $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock); - - $this->addArray($lineArray, $this->indent); - - foreach ($this->delayedPath as $indent => $delayedPath) - $this->path[$indent] = $delayedPath; - - $this->delayedPath = array(); - - } - return $this->result; - } - - private function loadFromSource ($input) { - if (!empty($input) && strpos($input, "\n") === false && file_exists($input)) - $input = file_get_contents($input); - - return $this->loadFromString($input); - } - - private function loadFromString ($input) { - $lines = explode("\n",$input); - foreach ($lines as $k => $_) { - $lines[$k] = rtrim ($_, "\r"); - } - return $lines; - } - - /** - * Parses YAML code and returns an array for a node - * @access private - * @return array - * @param string $line A line from the YAML file - */ - private function _parseLine($line) { - if (!$line) return array(); - $line = trim($line); - if (!$line) return array(); - - $array = array(); - - $group = $this->nodeContainsGroup($line); - if ($group) { - $this->addGroup($line, $group); - $line = $this->stripGroup ($line, $group); - } - - if ($this->startsMappedSequence($line)) - return $this->returnMappedSequence($line); - - if ($this->startsMappedValue($line)) - return $this->returnMappedValue($line); - - if ($this->isArrayElement($line)) - return $this->returnArrayElement($line); - - if ($this->isPlainArray($line)) - return $this->returnPlainArray($line); - - - return $this->returnKeyValuePair($line); - - } - - /** - * Finds the type of the passed value, returns the value as the new type. - * @access private - * @param string $value - * @return mixed - */ - private function _toType($value) { - if ($value === '') return ""; - $first_character = $value[0]; - $last_character = substr($value, -1, 1); - - $is_quoted = false; - do { - if (!$value) break; - if ($first_character != '"' && $first_character != "'") break; - if ($last_character != '"' && $last_character != "'") break; - $is_quoted = true; - } while (0); - - if ($is_quoted) { - $value = str_replace('\n', "\n", $value); - if ($first_character == "'") - return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\'')); - return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\'')); - } - - if (strpos($value, ' #') !== false && !$is_quoted) - $value = preg_replace('/\s+#(.+)$/','',$value); - - if ($first_character == '[' && $last_character == ']') { - // Take out strings sequences and mappings - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $value = array(); - foreach ($explode as $v) { - $value[] = $this->_toType($v); - } - return $value; - } - - if (strpos($value,': ')!==false && $first_character != '{') { - $array = explode(': ',$value); - $key = trim($array[0]); - array_shift($array); - $value = trim(implode(': ',$array)); - $value = $this->_toType($value); - return array($key => $value); - } - - if ($first_character == '{' && $last_character == '}') { - $innerValue = trim(substr ($value, 1, -1)); - if ($innerValue === '') return array(); - // Inline Mapping - // Take out strings sequences and mappings - $explode = $this->_inlineEscape($innerValue); - // Propagate value array - $array = array(); - foreach ($explode as $v) { - $SubArr = $this->_toType($v); - if (empty($SubArr)) continue; - if (is_array ($SubArr)) { - $array[key($SubArr)] = $SubArr[key($SubArr)]; continue; - } - $array[] = $SubArr; - } - return $array; - } - - if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') { - return null; - } - - if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){ - $intvalue = (int)$value; - if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX) - $value = $intvalue; - return $value; - } - - if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) { - // Hexadecimal value. - return hexdec($value); - } - - $this->coerceValue($value); - - if (is_numeric($value)) { - if ($value === '0') return 0; - if (rtrim ($value, 0) === $value) - $value = (float)$value; - return $value; - } - - return $value; - } - - /** - * Used in inlines to check for more inlines or quoted strings - * @access private - * @return array - */ - private function _inlineEscape($inline) { - // There's gotta be a cleaner way to do this... - // While pure sequences seem to be nesting just fine, - // pure mappings and mappings with sequences inside can't go very - // deep. This needs to be fixed. - - $seqs = array(); - $maps = array(); - $saved_strings = array(); - $saved_empties = array(); - - // Check for empty strings - $regex = '/("")|(\'\')/'; - if (preg_match_all($regex,$inline,$strings)) { - $saved_empties = $strings[0]; - $inline = preg_replace($regex,'YAMLEmpty',$inline); - } - unset($regex); - - // Check for strings - $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; - if (preg_match_all($regex,$inline,$strings)) { - $saved_strings = $strings[0]; - $inline = preg_replace($regex,'YAMLString',$inline); - } - unset($regex); - - // echo $inline; - - $i = 0; - do { - - // Check for sequences - while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) { - $seqs[] = $matchseqs[0]; - $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1); - } - - // Check for mappings - while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) { - $maps[] = $matchmaps[0]; - $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1); - } - - if ($i++ >= 10) break; - - } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false); - - $explode = explode(',',$inline); - $explode = array_map('trim', $explode); - $stringi = 0; $i = 0; - - while (1) { - - // Re-add the sequences - if (!empty($seqs)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - foreach ($seqs as $seqk => $seq) { - $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value); - $value = $explode[$key]; - } - } - } - } - - // Re-add the mappings - if (!empty($maps)) { - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLMap') !== false) { - foreach ($maps as $mapk => $map) { - $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value); - $value = $explode[$key]; - } - } - } - } - - - // Re-add the strings - if (!empty($saved_strings)) { - foreach ($explode as $key => $value) { - while (strpos($value,'YAMLString') !== false) { - $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1); - unset($saved_strings[$stringi]); - ++$stringi; - $value = $explode[$key]; - } - } - } - - - // Re-add the empties - if (!empty($saved_empties)) { - foreach ($explode as $key => $value) { - while (strpos($value,'YAMLEmpty') !== false) { - $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1); - $value = $explode[$key]; - } - } - } - - $finished = true; - foreach ($explode as $key => $value) { - if (strpos($value,'YAMLSeq') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLMap') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLString') !== false) { - $finished = false; break; - } - if (strpos($value,'YAMLEmpty') !== false) { - $finished = false; break; - } - } - if ($finished) break; - - $i++; - if ($i > 10) - break; // Prevent infinite loops. - } - - - return $explode; - } - - private function literalBlockContinues ($line, $lineIndent) { - if (!trim($line)) return true; - if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true; - return false; - } - - private function referenceContentsByAlias ($alias) { - do { - if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; } - $groupPath = $this->SavedGroups[$alias]; - $value = $this->result; - foreach ($groupPath as $k) { - $value = $value[$k]; - } - } while (false); - return $value; - } - - private function addArrayInline ($array, $indent) { - $CommonGroupPath = $this->path; - if (empty ($array)) return false; - - foreach ($array as $k => $_) { - $this->addArray(array($k => $_), $indent); - $this->path = $CommonGroupPath; - } - return true; - } - - private function addArray ($incoming_data, $incoming_indent) { - - // print_r ($incoming_data); - - if (count ($incoming_data) > 1) - return $this->addArrayInline ($incoming_data, $incoming_indent); - - $key = key ($incoming_data); - $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null; - if ($key === '__!YAMLZero') $key = '0'; - - if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values. - if ($key || $key === '' || $key === '0') { - $this->result[$key] = $value; - } else { - $this->result[] = $value; end ($this->result); $key = key ($this->result); - } - $this->path[$incoming_indent] = $key; - return; - } - - - - $history = array(); - // Unfolding inner array tree. - $history[] = $_arr = $this->result; - foreach ($this->path as $k) { - $history[] = $_arr = $_arr[$k]; - } - - if ($this->_containsGroupAlias) { - $value = $this->referenceContentsByAlias($this->_containsGroupAlias); - $this->_containsGroupAlias = false; - } - - - // Adding string or numeric key to the innermost level or $this->arr. - if (is_string($key) && $key == '<<') { - if (!is_array ($_arr)) { $_arr = array (); } - - $_arr = array_merge ($_arr, $value); - } else if ($key || $key === '' || $key === '0') { - if (!is_array ($_arr)) - $_arr = array ($key=>$value); - else - $_arr[$key] = $value; - } else { - if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; } - else { $_arr[] = $value; end ($_arr); $key = key ($_arr); } - } - - $reverse_path = array_reverse($this->path); - $reverse_history = array_reverse ($history); - $reverse_history[0] = $_arr; - $cnt = count($reverse_history) - 1; - for ($i = 0; $i < $cnt; $i++) { - $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i]; - } - $this->result = $reverse_history[$cnt]; - - $this->path[$incoming_indent] = $key; - - if ($this->_containsGroupAnchor) { - $this->SavedGroups[$this->_containsGroupAnchor] = $this->path; - if (is_array ($value)) { - $k = key ($value); - if (!is_int ($k)) { - $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k; - } - } - $this->_containsGroupAnchor = false; - } - - } - - private static function startsLiteralBlock ($line) { - $lastChar = substr (trim($line), -1); - if ($lastChar != '>' && $lastChar != '|') return false; - if ($lastChar == '|') return $lastChar; - // HTML tags should not be counted as literal blocks. - if (preg_match ('#<.*?>$#', $line)) return false; - return $lastChar; - } - - private static function greedilyNeedNextLine($line) { - $line = trim ($line); - if (!strlen($line)) return false; - if (substr ($line, -1, 1) == ']') return false; - if ($line[0] == '[') return true; - if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true; - return false; - } - - private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) { - $line = self::stripIndent($line, $indent); - if ($literalBlockStyle !== '|') { - $line = self::stripIndent($line); - } - $line = rtrim ($line, "\r\n\t ") . "\n"; - if ($literalBlockStyle == '|') { - return $literalBlock . $line; - } - if (strlen($line) == 0) - return rtrim($literalBlock, ' ') . "\n"; - if ($line == "\n" && $literalBlockStyle == '>') { - return rtrim ($literalBlock, " \t") . "\n"; - } - if ($line != "\n") - $line = trim ($line, "\r\n ") . " "; - return $literalBlock . $line; - } - - function revertLiteralPlaceHolder ($lineArray, $literalBlock) { - foreach ($lineArray as $k => $_) { - if (is_array($_)) - $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock); - else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder) - $lineArray[$k] = rtrim ($literalBlock, " \r\n"); - } - return $lineArray; - } - - private static function stripIndent ($line, $indent = -1) { - if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line)); - return substr ($line, $indent); - } - - private function getParentPathByIndent ($indent) { - if ($indent == 0) return array(); - $linePath = $this->path; - do { - end($linePath); $lastIndentInParentPath = key($linePath); - if ($indent <= $lastIndentInParentPath) array_pop ($linePath); - } while ($indent <= $lastIndentInParentPath); - return $linePath; - } - - - private function clearBiggerPathValues ($indent) { - - - if ($indent == 0) $this->path = array(); - if (empty ($this->path)) return true; - - foreach ($this->path as $k => $_) { - if ($k > $indent) unset ($this->path[$k]); - } - - return true; - } - - - private static function isComment ($line) { - if (!$line) return false; - if ($line[0] == '#') return true; - if (trim($line, " \r\n\t") == '---') return true; - return false; - } - - private static function isEmpty ($line) { - return (trim ($line) === ''); - } - - - private function isArrayElement ($line) { - if (!$line || !is_scalar($line)) return false; - if (substr($line, 0, 2) != '- ') return false; - if (strlen ($line) > 3) - if (substr($line,0,3) == '---') return false; - - return true; - } - - private function isHashElement ($line) { - return strpos($line, ':'); - } - - private function isLiteral ($line) { - if ($this->isArrayElement($line)) return false; - if ($this->isHashElement($line)) return false; - return true; - } - - - private static function unquote ($value) { - if (!$value) return $value; - if (!is_string($value)) return $value; - if ($value[0] == '\'') return trim ($value, '\''); - if ($value[0] == '"') return trim ($value, '"'); - return $value; - } - - private function startsMappedSequence ($line) { - return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':'); - } - - private function returnMappedSequence ($line) { - $array = array(); - $key = self::unquote(trim(substr($line,1,-1))); - $array[$key] = array(); - $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key); - return array($array); - } - - private function checkKeysInValue($value) { - if (strchr('[{"\'', $value[0]) === false) { - if (strchr($value, ': ') !== false) { - throw new Exception('Too many keys: '.$value); - } - } - } - - private function returnMappedValue ($line) { - $this->checkKeysInValue($line); - $array = array(); - $key = self::unquote (trim(substr($line,0,-1))); - $array[$key] = ''; - return $array; - } - - private function startsMappedValue ($line) { - return (substr ($line, -1, 1) == ':'); - } - - private function isPlainArray ($line) { - return ($line[0] == '[' && substr ($line, -1, 1) == ']'); - } - - private function returnPlainArray ($line) { - return $this->_toType($line); - } - - private function returnKeyValuePair ($line) { - $array = array(); - $key = ''; - if (strpos ($line, ': ')) { - // It's a key/value pair most likely - // If the key is in double quotes pull it out - if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { - $value = trim(str_replace($matches[1],'',$line)); - $key = $matches[2]; - } else { - // Do some guesswork as to the key and the value - $explode = explode(': ', $line); - $key = trim(array_shift($explode)); - $value = trim(implode(': ', $explode)); - $this->checkKeysInValue($value); - } - // Set the type of the value. Int, string, etc - $value = $this->_toType($value); - if ($key === '0') $key = '__!YAMLZero'; - $array[$key] = $value; - } else { - $array = array ($line); - } - return $array; - - } - - - private function returnArrayElement ($line) { - if (strlen($line) <= 1) return array(array()); // Weird %) - $array = array(); - $value = trim(substr($line,1)); - $value = $this->_toType($value); - if ($this->isArrayElement($value)) { - $value = $this->returnArrayElement($value); - } - $array[] = $value; - return $array; - } - - - private function nodeContainsGroup ($line) { - $symbolsForReference = 'A-z0-9_\-'; - if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-) - if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1]; - if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1]; - if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1]; - if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1]; - return false; - - } - - private function addGroup ($line, $group) { - if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1); - if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1); - //print_r ($this->path); - } - - private function stripGroup ($line, $group) { - $line = trim(str_replace($group, '', $line)); - return $line; - } -} -} - -// Enable use of Spyc from command line -// The syntax is the following: php Spyc.php spyc.yaml - -do { - if (PHP_SAPI != 'cli') break; - if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break; - if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break; - $file = $argv[1]; - echo json_encode (spyc_load_file ($file)); -} while (0); diff --git a/util/Text.class.php b/util/Text.class.php @@ -1,393 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002 Jan Dankert, jandankert@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - -/** - * Nuetzliche Funktionen fuer das Bearbeiten von Texten/Zeichenketten - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Text -{ - /** - * - * @param unknown $key - * @param unknown $text - * @return string|unknown - */ - public static function accessKey( $key, $text ) - { - $pos = strpos(strtolower($text),strtolower($key)); - - if ( $pos !== false ) - return substr($text,0,max($pos,0)).'<span class="accesskey">'.substr($text,$pos,1).'</span>'.substr($text,$pos+1); - else - return $text; - } - - /** - * Alias fuer Methode maxLength() - * - * @deprecated use maxlength() ! - */ - public static function maxLaenge( $laenge,$text ) - { - return Text::maxLength($text,$laenge); - } - - - /** - * Einen Text auf eine bestimmte Laenge begrenzen. - * - * Ist der Text zu lang, so wird abgeschnitten und - * eine Zeichenkette angehaengt. - * - * @param String Text, der zu begrenzen ist - * @param Integer maximale Laenge des Textes (optional) - * @param Text, der an gekuerzten Text angehangen wird (optional) - */ - public static function maxLength( $text,$laenge=20,$append='...',$where=STR_PAD_RIGHT ) - { - if ( strlen($text) > $laenge ) - { - if ( $where == STR_PAD_RIGHT ) - $text = substr($text,0,$laenge).$append; - elseif ( $where == STR_PAD_BOTH ) - $text = substr($text,0,$laenge/2).$append.substr($text,strlen($text)-($laenge/2)); - } - - return $text; - } - - - /** - * Umwandeln von BB-Code in Wiki-Textauszeichnungen - * - * @param text zu bearbeitender Text - * - * @return String Ausgabe - */ - public static function bbCode2Wiki( $inhalt ) - { - $inhalt = preg_replace('/\[b\]([^\[]*)\[\/b\]/i' , '*\\1*' ,$inhalt); - $inhalt = preg_replace('/\[i\]([^\[]*)\[\/i\]/i' , '_\\1_' ,$inhalt); - $inhalt = preg_replace('/\[code\]([^\[]*)\[\/code\]/i' , '=\\1=' ,$inhalt); - - $inhalt = preg_replace('/\[url\]([^\[]*)\[\/url\]/i' ,'"\\1"->"\\1"' ,$inhalt); - $inhalt = preg_replace('/\[url=([^\[]*)\]([^\[]*)\[\/url\]/i' ,'"\\2"->"\\1"' ,$inhalt); - - return $inhalt; - } - - - /** - * Umwandeln von einfachen HTML-Befehlen in Wiki-Textauszeichnungen - * - * @param text zu bearbeitender Text - * - * @return String Ausgabe - */ - public static function Html2Wiki( $inhalt ) - { - $inhalt = preg_replace('/<b(.*)>(.*)<\/b>/i','*\\2*' ,$inhalt); - $inhalt = preg_replace('/<i(.*)>(.*)<\/i>/i','_\\2_' ,$inhalt); - $inhalt = preg_replace('/<a(.*)href="(.*)">(.*)<\/a>/i','"\\3"->"\\2"' ,$inhalt); - - return $inhalt; - } - - - /** - * HTML-Entitaeten fuer HTML-Tags verwenden - * - * @param String Text, in dem HTML-Tags umgewandelt werden sollen - * @return String Ausgabe - */ - public static function encodeHtml( $inhalt ) - { - //$inhalt = str_replace('&','&amp;',$inhalt); - $inhalt = str_replace('"','&quot;',$inhalt); - $inhalt = str_replace('<','&lt;' ,$inhalt); - $inhalt = str_replace('>','&gt;' ,$inhalt); - - return $inhalt; - } - - - - /** - * Ersetzt Sonderzeichen durch HTML-�quivalente.<br> - * Z.B. Ersetzt "(c)" durch "&copy;". - */ - public static function replaceHtmlChars( $text ) - { - global $conf; - - foreach( explode(' ',$conf['editor']['html']['replace']) as $repl ) - { - list( $ersetze, $mit ) = explode(':',$repl.':'); - $text = str_replace($ersetze, $mit, $text); - } - - return $text; - } - - - - /** - * HTML-Entitaeten fuer HTML-Tags verwenden - * - * @param String Text, in dem HTML-Tags umgewandelt werden sollen - * @return String Ausgabe - */ - public static function encodeHtmlSpecialChars( $inhalt ) - { - return Text::replaceHtmlChars( $inhalt ); - } - - - - /** - * Vergleicht 2 Text-Arrays und ermittelt eine Darstellung der Unterschiede - * - */ - public static function diff( $from_text,$to_text ) - { - // Zaehler pro Textarray - $pos_from = -1; - $pos_to = -1; - - // Ergebnis-Arrays - $from_out = array(); - $to_out = array(); - - while( true ) - { - $pos_from++; - $pos_to ++; - - if ( !isset($from_text[$pos_from]) && - !isset($to_text [$pos_to ]) ) - { - // Text in ist 'neu' und 'alt' zuende. Ende der Schleife. - break; - } - elseif - ( isset($from_text[$pos_from]) && - !isset($to_text [$pos_to]) ) - { - // Text in 'neu' ist zuende, die Restzeilen von 'alt' werden ausgegeben - while( isset($from_text[$pos_from]) ) - { - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'old'); - $to_out [] = array(); - $pos_from++; - } - break; - } - elseif - ( !isset($from_text[$pos_from]) && - isset($to_text [$pos_to]) ) - { - // Umgekehrter Fall: Text in 'alt' ist zuende, Restzeilen aus 'neu' werden ausgegeben - while( isset($to_text[$pos_to]) ) - { - $from_out[] = array(); - $to_out [] = array('text'=>$to_text[$pos_to],'line'=>$pos_to+1,'type'=>'new'); - $pos_to++; - } - break; - } - elseif - ( rtrim($from_text[$pos_from]) != rtrim($to_text[$pos_to]) ) - { - // Zeilen sind vorhanden, aber ungleich - // Wir suchen jetzt die naechsten beiden Zeilen, die gleich sind. - $max_entf = min(count($from_text)-$pos_from-1,count($to_text)-$pos_to-1); - - #echo "suche start, max_entf=$max_entf, pos_from=$pos_from, pos_to=$pos_to<br/>"; - - for ( $a=0; $a<=$max_entf; $a++ ) - { - #echo "a ist $a<br/>"; - for ( $b=0; $b<=$max_entf; $b++ ) - { - #echo "b ist $b<br/>"; - if ( trim($from_text[$pos_from+$b]) != '' && - $from_text[$pos_from+$b] == $to_text[$pos_to+$a] ) - { - $pos_gef_from = $pos_from+$b; - $pos_gef_to = $pos_to +$a; - break; - } - - if ( trim($from_text[$pos_from+$a]) != '' && - $from_text[$pos_from+$a] == $to_text[$pos_to+$b] ) - { - $pos_gef_from = $pos_from+$a; - $pos_gef_to = $pos_to +$b; - break; - } - } - - if ( $b <=$max_entf) - { - break; - } - } - - if ( $a<=$max_entf ) - { - // Gleiche Zeile gefunden - #echo "gefunden, pos_gef_from=$pos_gef_from, pos_gef_to=$pos_gef_to<br/>"; - - if ( $pos_gef_from - $pos_from == 0 ) - $type = 'new'; - elseif - ( $pos_gef_to - $pos_to == 0 ) - $type = 'old'; - else - $type = 'notequal'; - - while( $pos_gef_from - $pos_from > 0 && - $pos_gef_to - $pos_to > 0 ) - { - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type); - $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type); - - $pos_from++; - $pos_to++; - } - - while( $pos_gef_from - $pos_from > 0 ) - { - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type); - $to_out [] = array(); - $pos_from++; - } - - while( $pos_gef_to - $pos_to > 0 ) - { - $from_out[] = array(); - $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type); - $pos_to++; - } - $pos_from--; - $pos_to--; - } - else - { - // Keine gleichen Zeilen gefunden - #echo "nicht gefunden, i=$i, j=$j, pos_from war $pos_from, pos_to war $pos_to<br/>"; - - while( true ) - { - if ( !isset($from_text[$pos_from]) && - !isset($to_text [$pos_to ]) ) - { - break; - } - elseif - ( isset($from_text[$pos_from]) && - !isset($to_text [$pos_to ]) ) - { - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal'); - $to_out [] = array(); - } - elseif - ( !isset($from_text[$pos_from]) && - isset($to_text [$pos_to ]) ) - { - $from_out[] = array(); - $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal'); - } - else - { - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal'); - $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal'); - } - $pos_from++; - $pos_to++; - } - } - } - else - { - // Zeilen sind gleich - $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'equal'); - $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'equal'); - } - } - - return( array($from_out,$to_out) ); - } - - - /** - * Entfernt einen Text-Bereich aus einer Zeichenkette.<br> - * Es wird angegeben, von wo bis wo entfernt werden soll. - * - * @param $text Text, aus dem entfernt wird - * @param $von der Text, AB dem entfernt wird - * @param $bis der Text, BIS ZU DEM entfernt wird - * @return String Text - */ - public static function entferneVonBis($text,$von,$bis) - { - $beg = strpos($text,$von); - $end = strpos($text,$bis); - if ( $beg!==false && $end !==false ) - $text = substr($text,0,$beg).substr($text,$end+strlen($bis)); - return $text; - } - - - /** - * Saeubert eine Zeichenkette. - * - * Es werden ungueltige Zeichen aus einer Zeichenkette entfernt. Es wird mit einer Whitelist - * gearbeitet, d.h. die erlaubten Zeichen werden angegeben. - * - * @param $eingabe Die Eingabe-Zeichenkette, aus der ungueltige Zeichen entfernt werden sollen. - * @param $erlaubt Die erlaubten Zeichen (eine "White-List") - * @return String die aufgeräumte Zeichenkette - */ - public static function clean( $eingabe, $erlaubt ) - { - $first = strtr( $eingabe, $erlaubt, str_repeat("\x01", strlen($erlaubt)) ); - $second = strtr( $eingabe, $first , str_repeat("\x00", strlen($first )) ); - return str_replace("\x00",'',$second); - } - - - - public static function parseOID( $text ) - { - $oids = array(); - $treffer = array(); - - preg_match_all('/\"([^\"]*)__OID__([0-9]+)__([^\"]*)\"/', $text, $treffer,PREG_SET_ORDER); - - foreach( $treffer as $t ) - $oids[$t[2]] = $t[0]; - - return $oids; - } -} - diff --git a/util/Transformer.class.php b/util/Transformer.class.php @@ -1,102 +0,0 @@ -<?php - -/** - * Transformieren eines Textes.<br> - * Ein Text wird geparst und neu gerendert. - * - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Transformer -{ - var $text = ''; - var $doc; - var $page; - var $element; - - function transform() - { - $this->parseDocument(); - $this->renderDocument(); - - $this->text = $this->renderedText; - } - - - - /** - * Parsen eines Textes.<br> - * Der Text muss in der Eigenschaft 'text' bereits zur Verf�gung stehen.<br> - * Der Text wird geparst und als DOM (Document object model) intern gespeichert. - */ - - function parseDocument() - { - // Den Text zeilenweise aufteilen. - $zeilen = explode("\n",$this->text); - - // Dokument erzeugen und den Text parsen. - $parser = new WikiParser(); - $this->doc = new DocumentElement(); - $this->doc->element = $this->element; - $this->doc->parse( $zeilen ); - $this->doc->page = $this->page; - } - - - - /** - * Das interne Dokumente wird gerendet.<br> - * Die fertige Ausgabe steht anschliessend in der Eigenschaft "renderedText" zur Verf�gung. - */ - function renderDocument() - { - $this->doc->encodeHtml = !$this->element->html; - - $text = $this->doc->render( $this->page->mimeType() ); - - // Liste der verlinkten Objekt-Ids. - // Die Objekt-Ids werden absteigend sortiert, damit z.B. '33' vor '3' ersetzt wird. - $linkedObjectIds = $this->doc->linkedObjectIds; - rsort( $linkedObjectIds,SORT_NUMERIC ); - - // Links object:nnn ersetzen - // - // Das Dokument-Objekt hat keine Information ueber die aktuelle Seite, - // daher werden die Links auf Objekte hier gesetzt. - foreach( $linkedObjectIds as $objectId ) - { - $targetPath = $this->page->path_to_object( $objectId ); - - // Hack: Sonderzeichen muessen in URLs maskiert werden, aber nur bei URLs die aus Link-Objekten kommen, bei allem - // anderen (insbesondere Preview-Links zu andereen Seiten) darf die Umsetzung nicht erfolgen. - // Der Renderer kann dies nicht tun, denn der erzeugt nur "object://..."-URLs. - // Beispiel: "...?a=1&b=2" wird zu "...?a=1&amp;b=2" - $o = new Object($objectId); - try - { - $o->load(); - if ( $o->isLink ) - { - $l = new Link($objectId); - $l->load(); - if ( $l->isLinkToUrl && $this->page->mimeType() == 'text/html' ) - $targetPath = htmlspecialchars($targetPath); - } - } - catch( ObjectNotFoundException $e) - { - $targetPath = 'javascript:alert("object '.$objectId.' not found");'; - } - - - $text = str_replace( 'object:' .$objectId, $targetPath, $text ); - $text = str_replace( 'object://'.$objectId, $targetPath, $text ); - } - - $this->renderedText = $text; - } -} - -?>- \ No newline at end of file diff --git a/util/TreeElement.class.php b/util/TreeElement.class.php @@ -1,75 +0,0 @@ -<?php - -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Darstellen eines Elementes in einer Baumstruktur - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class TreeElement -{ - /** - * @type Integer - */ - var $id; - - var $extraId = array(); - - var $internalId = 0; - - /** - * Text des Baumelementes - * @type String - */ - var $text = ""; - - /** - * Beschreibung - * @type String - */ - var $description = ""; - var $url = ""; - var $icon = ""; - var $target = ""; - var $action = ""; - - /** - * Unterelemente - * Ein Array von Ids - * @type Array - */ - var $subElementIds = array(); - - /** - * Typ des Elementes - * In der Tree-Klasse muss es eine Methode mit diesem Namen geben, die das - * Element laedt. - * @type String - */ - var $type = ""; - - - // Konstruktor - function __construct() - { - } -} - -?>- \ No newline at end of file diff --git a/util/Upload.class.php b/util/Upload.class.php @@ -1,85 +0,0 @@ -<?php -// OpenRat Content Management System -// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de -// -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -/** - * Methoden fuer den Upload einer Datei - * - * @author $Author$ - * @version $Revision$ - * @package openrat.services - */ -class Upload -{ - var $filename; - var $extension; - var $value; - var $size; - var $error = ''; - - - /** - * Stellt fest, ob der Upload geklappt hat. - * - * @return boolean - */ - function isValid() - { - return empty($this->error); - } - - - - /** - * Bearbeitet den Upload einer Datei.<br> - * Bei der Objekterzeugung wird die Datei bereits geladen.<br> - * - * @return Upload - */ - function __construct( $name='file' ) // Konstruktor - { - global $FILES; - - if ( !isset($FILES[$name]) || - !isset($FILES[$name]['tmp_name']) || - !is_file($FILES[$name]['tmp_name']) ) - { - $this->error = 'No file received.'; - return; - } - - $this->size = filesize($FILES[$name]['tmp_name']); - - $fh = fopen( $FILES[$name]['tmp_name'],'r' ); - - $this->value = fread($fh,$this->size); - fclose( $fh ); - - $this->filename = $FILES[$name]['name']; - $this->extension = ''; - - $p = strrpos( $this->filename,'.' ); // Letzten Punkt suchen - - if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen - { - $this->extension = substr( $this->filename,$p+1 ); - $this->filename = substr( $this->filename,0,$p ); - } - } -} - -?>- \ No newline at end of file diff --git a/util/XML.class.php b/util/XML.class.php @@ -1,169 +0,0 @@ -<?php -/** - * Multidimensional Array-to-XML. - * - * Example: - * $xml = new XML(); - * header('Content-Type: application/xml'); - * echo $xml->encode( $yourBigArray ); - * exit; - * - * Author: Honor� Vasconcelos, Jan Dankert - * - * Original from: - * Clean XML To Array: http://www.phpclasses.org/browse/package/3598.html - * - * License of this class: BSD-Licence. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list - * of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this - * list of conditions and the following disclaimer in the documentation and/or other - * materials provided with the distribution. - * - * Neither the name of the Author(s) nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -class XML -{ - /** - * Parse multidimentional array to XML. - * - * @param array $array - * @return String - */ - var $xmlText = ''; - - - /** - * Name of the root element. - * - * @var String - */ - var $root = 'xml'; - - /* - * Char to indent with. - * - * @var String - */ - var $indentChar = "\t"; - - - /** - * Newline-Char - * @var String - */ - var $nl = "\n"; - - /** - * Encode a array to XML. - * - * @param Array $array - * @return String (serialized XML) - */ - function encode($array) - { - //star and end the XML document - $this->xmlText = '<?xml version="1.0" encoding="utf-8"?>'.$this->nl; - $this->xmlText .= '<'.$this->root.'>'.$this->nl; - $this->array_transform($array,1); - $this->xmlText .='</'.$this->root.'>'; - - return $this->xmlText; - } - - - /** - * @access private - */ - function array_transform($array,$depth){ - - foreach($array as $key => $value) - { - $attr = array(); - if ( is_numeric($key) ) - { - // Array-Einträge mit numerischen Index können nicht direkt in ein XML-Element - // umgewandelt werden, da nur-numerische Element-Namen nicht erlaubt sind. - // Daher verwenden wir dann 'entry' als Elementnamen. - $attr['id'] = $key; - $key = 'entry'; - } - - $indent = str_repeat($this->indentChar,$depth); - - if ( empty($value) ) - { - $this->xmlText .= $indent.$this->shortTag($key,$attr).$this->nl; - } - elseif ( is_object($value) ) - { - // Der Inhalt ist ein Array, daher rekursiv verzweigen. - $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl; - $prop = get_object_vars($value); - $this->array_transform($prop,$depth+1); // Rekursiver Aufruf - $this->xmlText .= $indent.$this->closeTag($key).$this->nl; - } - elseif ( is_array($value) ) - { - // Der Inhalt ist ein Array, daher rekursiv verzweigen. - $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl; - $this->array_transform($value,$depth+1); // Rekursiver Aufruf - $this->xmlText .= $indent.$this->closeTag($key).$this->nl; - } - else - { - // Der Inhalt ist ein einfacher Inhalt (kein Array). - $this->xmlText .= $indent.$this->openTag($key,$attr); - $this->xmlText .= $value; - $this->xmlText .= $this->closeTag($key).$this->nl; - } - } - } - - - function openTag($key,$attr) - { - $tag = '<'.$key; - foreach( $attr as $attr_name=>$attr_value ) - $tag .= ' '.$attr_name.'="'.$attr_value.'"'; - $tag .= '>'; - return $tag; - } - - - - function shortTag($key,$attr) - { - $tag = '<'.$key; - foreach( $attr as $attr_name=>$attr_value ) - $tag .= ' '.$attr_name.'="'.$attr_value.'"'; - $tag .= ' />'; - return $tag; - } - - - - function closeTag($key) - { - return '</'.$key.'>'; - } -} - -?>- \ No newline at end of file diff --git a/util/config-default.php b/util/config-default.php @@ -1,880 +0,0 @@ -<?php -// DO NOT MAKE ANY CHANGES IN THIS FILE, please edit the file 'config.yml' or 'config-<host>.yml' instead. -// This file should only be changed by developers. -$conf = array(); -$conf['applications'] = array(); -$conf['applications']['']=0; -$conf['applications']['phpmyadmin'] = array(); -$conf['applications']['phpmyadmin']['name']='PHPYourAdmin'; -$conf['applications']['phpmyadmin']['url']="https://example.com/anotherapplication/index.cgi"; -$conf['applications']['phpmyadmin']['param']="ticketidforopenrat"; -$conf['applications']['phpmyadmin']['group']='0'; -$conf['applications']['phpmyadmin']['description']="Your database administration"; -$conf['cache'] = array(); -$conf['cache']['conditional_get']=true; -$conf['cache']['enable_cache']=false; -$conf['cache']['tmp_dir']=""; -$conf['config'] = array(); -$conf['config']['auto_reload']= true; -$conf['config']['session_destroy_on_config_reload']= true; -$conf['content'] = array(); -$conf['content']['file'] = array(); -$conf['content']['file']['max_file_size']='1500'; -$conf['content']['revision-limit'] = array(); -$conf['content']['revision-limit']['enabled']= false; -$conf['content']['revision-limit']['max-age']= 120; -$conf['content']['revision-limit']['min-age']= 1; -$conf['content']['revision-limit']['max-revisions']= 100; -$conf['content']['revision-limit']['min-revisions']= 3; -$conf['content']['language'] = array(); -$conf['content']['language']['use_default_language']= true; -$conf['countries'] = array(); -$conf['countries']['']='0'; -$conf['countries']['AA']='Afar'; -$conf['countries']['AB']='Abkhazian'; -$conf['countries']['AF']='Afrikaans'; -$conf['countries']['AM']='Amharic'; -$conf['countries']['AR']='Arabic'; -$conf['countries']['AS']='Assamese'; -$conf['countries']['AY']='Aymara'; -$conf['countries']['AZ']='Azerbaijani'; -$conf['countries']['BA']='Bashkir'; -$conf['countries']['BE']='Byelorussian'; -$conf['countries']['BG']='Bulgarian'; -$conf['countries']['BH']='Bihari'; -$conf['countries']['BI']='Bislama'; -$conf['countries']['BN']='Bengali'; -$conf['countries']['BO']='Tibetan'; -$conf['countries']['BR']='Breton'; -$conf['countries']['CA']='Catalan'; -$conf['countries']['CO']='Corsican'; -$conf['countries']['CS']='Czech'; -$conf['countries']['CY']='Welsh'; -$conf['countries']['DA']='Danish'; -$conf['countries']['DE']='German'; -$conf['countries']['DZ']='Bhutani'; -$conf['countries']['EL']='Greek'; -$conf['countries']['EN']='English'; -$conf['countries']['EO']='Esperanto'; -$conf['countries']['ES']='Spanish'; -$conf['countries']['ET']='Estonian'; -$conf['countries']['EU']='Basque'; -$conf['countries']['FA']='Persian'; -$conf['countries']['FI']='Finnish'; -$conf['countries']['FJ']='Fiji'; -$conf['countries']['FO']='Faeroese'; -$conf['countries']['FR']='French'; -$conf['countries']['FY']='Frisian'; -$conf['countries']['GA']='Irish'; -$conf['countries']['GD']='Gaelic'; -$conf['countries']['GL']='Galician'; -$conf['countries']['GN']='Guarani'; -$conf['countries']['GU']='Gujarati'; -$conf['countries']['HA']='Hausa'; -$conf['countries']['HI']='Hindi'; -$conf['countries']['HR']='Croatian'; -$conf['countries']['HU']='Hungarian'; -$conf['countries']['HY']='Armenian'; -$conf['countries']['IA']='Interlingua'; -$conf['countries']['IE']='Interlingue'; -$conf['countries']['IK']='Inupiak'; -$conf['countries']['IN']='Indonesian'; -$conf['countries']['IS']='Icelandic'; -$conf['countries']['IT']='Italian'; -$conf['countries']['IW']='Hebrew'; -$conf['countries']['JA']='Japanese'; -$conf['countries']['JI']='Yiddish'; -$conf['countries']['JW']='Javanese'; -$conf['countries']['KA']='Georgian'; -$conf['countries']['KK']='Kazakh'; -$conf['countries']['KL']='Greenlandic'; -$conf['countries']['KM']='Cambodian'; -$conf['countries']['KN']='Kannada'; -$conf['countries']['KO']='Korean'; -$conf['countries']['KS']='Kashmiri'; -$conf['countries']['KU']='Kurdish'; -$conf['countries']['KY']='Kirghiz'; -$conf['countries']['LA']='Latin'; -$conf['countries']['LN']='Lingala'; -$conf['countries']['LO']='Laothian'; -$conf['countries']['LT']='Lithuanian'; -$conf['countries']['LV']='Latvian'; -$conf['countries']['MG']='Malagasy'; -$conf['countries']['MI']='Maori'; -$conf['countries']['MK']='Macedonian'; -$conf['countries']['ML']='Malayalam'; -$conf['countries']['MN']='Mongolian'; -$conf['countries']['MO']='Moldavian'; -$conf['countries']['MR']='Marathi'; -$conf['countries']['MS']='Malay'; -$conf['countries']['MT']='Maltese'; -$conf['countries']['MY']='Burmese'; -$conf['countries']['NA']='Nauru'; -$conf['countries']['NE']='Nepali'; -$conf['countries']['NL']='Dutch'; -$conf['countries']['_NO']='Norwegian'; -$conf['countries']['OC']='Occitan'; -$conf['countries']['OM']='Oromo'; -$conf['countries']['OR']='Oriya'; -$conf['countries']['PA']='Punjabi'; -$conf['countries']['PL']='Polish'; -$conf['countries']['PS']='Pashto'; -$conf['countries']['PT']='Portuguese'; -$conf['countries']['QU']='Quechua'; -$conf['countries']['RM']='Rhaeto-Romance'; -$conf['countries']['RN']='Kirundi'; -$conf['countries']['RO']='Romanian'; -$conf['countries']['RU']='Russian'; -$conf['countries']['RW']='Kinyarwanda'; -$conf['countries']['SA']='Sanskrit'; -$conf['countries']['SD']='Sindhi'; -$conf['countries']['SG']='Sangro'; -$conf['countries']['SH']='Serbo-Croatian'; -$conf['countries']['SI']='Singhalese'; -$conf['countries']['SK']='Slovak'; -$conf['countries']['SL']='Slovenian'; -$conf['countries']['SM']='Samoan'; -$conf['countries']['SN']='Shona'; -$conf['countries']['SO']='Somali'; -$conf['countries']['SQ']='Albanian'; -$conf['countries']['SR']='Serbian'; -$conf['countries']['SS']='Siswati'; -$conf['countries']['ST']='Sesotho'; -$conf['countries']['SU']='Sudanese'; -$conf['countries']['SV']='Swedish'; -$conf['countries']['SW']='Swahili'; -$conf['countries']['TA']='Tamil'; -$conf['countries']['TE']='Tegulu'; -$conf['countries']['TG']='Tajik'; -$conf['countries']['TH']='Thai'; -$conf['countries']['TI']='Tigrinya'; -$conf['countries']['TK']='Turkmen'; -$conf['countries']['TL']='Tagalog'; -$conf['countries']['TN']='Setswana'; -$conf['countries']['TO']='Tonga'; -$conf['countries']['TR']='Turkish'; -$conf['countries']['TS']='Tsonga'; -$conf['countries']['TT']='Tatar'; -$conf['countries']['TW']='Twi'; -$conf['countries']['UK']='Ukrainian'; -$conf['countries']['UR']='Urdu'; -$conf['countries']['UZ']='Uzbek'; -$conf['countries']['VI']='Vietnamese'; -$conf['countries']['VO']='Volapuk'; -$conf['countries']['WO']='Wolof'; -$conf['countries']['XH']='Xhosa'; -$conf['countries']['YO']='Yoruba'; -$conf['countries']['ZH']='Chinese'; - -$conf['database'] = array(); - -$conf['database-default']=array(); -$conf['database-default']['defaults']=array(); -$conf['database-default']['defaults']['prefix' ] = ''; -$conf['database-default']['defaults']['suffix' ] = ''; -$conf['database-default']['defaults']['enabled' ] = true; -$conf['database-default']['defaults']['name' ] = ''; -$conf['database-default']['defaults']['description'] = ''; -$conf['database-default']['defaults']['type' ] = 'pdo'; -$conf['database-default']['defaults']['dsn' ] = 'mysql:localhost'; -$conf['database-default']['defaults']['user' ] = ''; -$conf['database-default']['defaults']['password' ] = ''; -$conf['database-default']['defaults']['host' ] = ''; -$conf['database-default']['defaults']['database' ] = ''; -$conf['database-default']['defaults']['base64' ] = false; -$conf['database-default']['defaults']['persistent' ] = true; -$conf['database-default']['defaults']['charset' ] = 'UTF-8'; -$conf['database-default']['defaults']['connection_sql'] = ''; -$conf['database-default']['defaults']['cmd' ] = ''; -$conf['database-default']['defaults']['prepare' ] = true; -$conf['database-default']['defaults']['transaction'] = true; -$conf['database-default']['defaults']['update' ] = array(); -$conf['database-default']['defaults']['auto_update'] = true; -$conf['date'] = array(); -$conf['date']['format'] = array(); -$conf['date']['format']['SHORT']= ""; -$conf['date']['format']['ISO8601SHORT']= "Ymd"; -$conf['date']['format']['ISO8601']= "Y-m-d"; -$conf['date']['format']['ISO8601BAS']= "YmdTHis"; -$conf['date']['format']['ISO8601EXT']= "Y-m-dTH:i:s"; -$conf['date']['format']['ISO8601FULL']= "Y-m-dTH:i:sO"; -$conf['date']['format']['ISO8601WEEK']= "YWW"; -$conf['date']['format']['GER1']= "d.m.Y"; -$conf['date']['format']['GER2']= "d.m.Y, H:i"; -$conf['date']['format']['GER3']= "d.m.Y, H:i:s"; -$conf['date']['format']['GER4']= "d. F Y, H:i:s"; -$conf['date']['format']['ENGLONG']= "l dS of F Y h:i:s A"; -$conf['date']['format']['GMDATE']= "D, d M Y H:i:s GMT"; -$conf['date']['format']['RFC822']= "r"; -$conf['date']['format']['UNIX']= "U"; -$conf['date']['format']['LONG']= "F j, Y, g:i a"; -$conf['date']['timezone'] = array(); -$conf['date']['timezone']['-6']="New York"; -$conf['date']['timezone']['0']="UTC (GMT)"; -$conf['date']['timezone']['60']="MET (Middle European Time)"; -$conf['date']['timezone']['120']="MEST (Middle European Summertime)"; -$conf['editor'] = array(); -$conf['editor']['text-markup'] = array(); -$conf['editor']['text-markup']['strong-begin']= "*"; -$conf['editor']['text-markup']['strong-end']= "*"; -$conf['editor']['text-markup']['emphatic-begin']= "_"; -$conf['editor']['text-markup']['emphatic-end']= "_"; -$conf['editor']['text-markup']['image-begin']= "{"; -$conf['editor']['text-markup']['image-end']= "}"; -$conf['editor']['text-markup']['speech-begin']='QUOTE'; -$conf['editor']['text-markup']['speech-end']='QUOTE'; -$conf['editor']['text-markup']['code-begin']= "="; -$conf['editor']['text-markup']['code-end']= "="; -$conf['editor']['text-markup']['footnote-begin']= "["; -$conf['editor']['text-markup']['footnote-end']= "]"; -$conf['editor']['text-markup']['pre-begin']= "="; -$conf['editor']['text-markup']['pre-end']= "="; -$conf['editor']['text-markup']['insert-begin']= "++"; -$conf['editor']['text-markup']['insert-end']= "++"; -$conf['editor']['text-markup']['remove-begin']= "--"; -$conf['editor']['text-markup']['remove-end']= "--"; -$conf['editor']['text-markup']['definition-sep']= "::"; -$conf['editor']['text-markup']['headline']= "+"; -$conf['editor']['text-markup']['headline_level1_underline']= "="; -$conf['editor']['text-markup']['headline_level2_underline']= "-"; -$conf['editor']['text-markup']['headline_level3_underline']= "."; -$conf['editor']['text-markup']['list-unnumbered']= "-"; -$conf['editor']['text-markup']['list-numbered']= "#"; -$conf['editor']['text-markup']['table-of-content']= "##TOC##"; -$conf['editor']['text-markup']['linkto']= "->"; -$conf['editor']['text-markup']['table-cell-sep']= "|"; -$conf['editor']['text-markup']['style-begin']= "'"; -$conf['editor']['text-markup']['style-end']= "'"; -$conf['editor']['text-markup']['quote']= ">"; -$conf['editor']['text-markup']['quote-line-begin']= ">"; -$conf['editor']['text-markup']['quote-line-end']= ">"; -$conf['editor']['text-markup']['macro-begin']= "<<"; -$conf['editor']['text-markup']['macro-end']= ">>"; -$conf['editor']['text-markup']['macro-attribute-quote']= "'"; -$conf['editor']['text-markup']['macro-attribute-value-seperator']= "="; -$conf['editor']['html'] = array(); -$conf['editor']['html']['tag_strong']= "strong"; -$conf['editor']['html']['tag_emphatic']= "em"; -$conf['editor']['html']['tag_teletype']= "tt"; -$conf['editor']['html']['tag_speech']= "cite"; -$conf['editor']['html']['override_speech']=false; -$conf['editor']['html']['override_speech_open']= "&laquo;"; -$conf['editor']['html']['override_speech_close']= "&raquo;"; -$conf['editor']['html']['rendermode']="sgml"; -$conf['editor']['html']['rendermode']="xml"; -$conf['editor']['html']['replace']= "EUR:&euro;"; -$conf['editor']['wiki'] = array(); -$conf['editor']['wiki']['convert_html']=true; -$conf['editor']['wiki']['convert_bbcode']=true; -$conf['editor']['text'] = array(); -$conf['editor']['text']['linelength']='70'; -$conf['editor']['calendar'] = array(); -$conf['editor']['calendar']['weekday_offset']='1'; -$conf['editor']['text'] = array(); -$conf['editor']['text']['linelength']='70'; -$conf['editor']['macro'] = array(); -$conf['editor']['macro']['show_errors']=false; -$conf['filename'] = array(); -$conf['filename']['edit']=true; -$conf['filename']['default']='index'; -$conf['filename']['style']='short'; -$conf['filename']['url']='relative'; -$conf['ftp'] = array(); -$conf['ftp']['ascii']= "html,htm,php"; -$conf['help'] = array(); -$conf['help']['enabled']=true; -$conf['help']['url']="http://help.openrat.de/"; -$conf['help']['suffix']=".html"; -$conf['html'] = array(); -$conf['html']['tag_teletype']='tt'; -$conf['html']['tag_emphatic']='em'; -$conf['html']['tag_strong']='strong'; -$conf['html']['tag_speech']='cite'; -$conf['html']['speech_open']= "&bdquo"; -$conf['html']['speech_close']= "&rdquo"; -$conf['i18n'] = array(); -$conf['i18n']['use_http']=true; -$conf['i18n']['default']='de'; -$conf['i18n']['available']='de,en,es,fr,it,ru,cn'; -$conf['i18n']['locale'] = array(); -$conf['i18n']['locale']['de']="de_DE.utf8"; -$conf['i18n']['locale']['en']="en_US.utf8"; -$conf['image'] = array(); -$conf['image']['truecolor']=true; -$conf['interface'] = array(); -$conf['interface']['tree_width']= "25%"; -$conf['interface']['file_separator']= " &raquo"; -$conf['interface']['nice_urls']=false; -$conf['interface']['url_sessionid']=false; -$conf['interface']['theme']= 'default'; -$conf['interface']['timeout']='0'; -$conf['interface']['override_title']=''; -$conf['interface']['style'] = array(); -$conf['interface']['style']['default']='modern'; -$conf['interface']['config'] = array(); -$conf['interface']['config']['file_manager_url']=""; -$conf['interface']['config']['enable']=true; -$conf['interface']['config']['show_system']=true; -$conf['interface']['config']['show_interpreter']=true; -$conf['interface']['config']['show_extensions']=true; -$conf['interface']['frames'] = array(); -$conf['interface']['frames']['top']='_top'; -$conf['interface']['url'] = array(); -$conf['interface']['url']['fake_url']=false; -$conf['interface']['url']['index']=false; -$conf['interface']['url']['url_format']= "%s,%s.%i"; -$conf['interface']['url']['url_format']= "%s,%s,%d.do"; -$conf['interface']['url']['add_sessionid']=false; -$conf['interface']['gravatar'] = array(); -$conf['interface']['gravatar']['enable']=true; -$conf['interface']['gravatar']['size']='80'; -$conf['interface']['gravatar']['default']='404'; -$conf['interface']['gravatar']['rating']='g'; -$conf['interface']['session'] = array(); -$conf['interface']['session']['auto_extend']=true; -$conf['ldap'] = array(); -$conf['ldap']['host']="localhost"; -$conf['ldap']['port']="389"; -$conf['ldap']['protocol']="2"; -$conf['ldap']['dn']= "uid={user},ou=users,dc=example,dc=com"; -$conf['ldap']['dn']= ""; -$conf['ldap']['search'] = array(); -$conf['ldap']['search']['anonymous']=true; -$conf['ldap']['search']['user']= "uid=openrat,ou=users,dc=example,dc=com"; -$conf['ldap']['search']['password']= "verysecret"; -$conf['ldap']['search']['basedn']= "dc=example,dc=com"; -$conf['ldap']['search']['filter']= "(uid={user})"; -$conf['ldap']['search']['aliases']=true; -$conf['ldap']['search']['timeout']= 30; -$conf['ldap']['search']['add']=true; -$conf['ldap']['authorize'] = array(); -$conf['ldap']['authorize']['group_filter']="(memberUid={dn})"; -$conf['ldap']['authorize']['group_name']="cn"; -$conf['ldap']['authorize']['auto_add']=true; -$conf['login'] = array(); -$conf['login']['motd']=''; -$conf['login']['nologin']=false; -$conf['login']['register']=false; -$conf['login']['send_password']=false; -$conf['login']['gpl'] = array(); -$conf['login']['gpl']['url']="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"; -$conf['login']['logo'] = array(); -$conf['login']['logo']['enabled']=false; -$conf['login']['logo']['image']="./themes/default/images/logo.jpg" ; -$conf['login']['logo']['url']="http://www.openrat.de" ; -$conf['login']['start'] = array(); -$conf['login']['start']['start_lastchanged_object']=true; -$conf['login']['start']['start_single_project']=true; -$conf['login']['default-database']='db'; -$conf['log'] = array(); -$conf['log']['file']= null; -$conf['log']['level']= "warn"; -$conf['log']['date_format']= "M j H:i:s"; -$conf['log']['ns_lookup']=false; -$conf['log']['format']= "%time %level %host %user %action %text"; -$conf['mail'] = array(); -$conf['mail']['enabled']=true; -$conf['mail']['from']="OpenRat <user@example.com>"; -$conf['mail']['signature']="http://www.openrat.de"; -$conf['mail']['cc']='0'; -$conf['mail']['bcc']='0'; -$conf['mail']['priority']='3'; -$conf['mail']['header_encoding']="Quoted-printable"; -$conf['mail']['client']='smtp'; -$conf['mail']['client']='php'; -$conf['mail']['whitelist']= ""; -$conf['mail']['blacklist']= ""; -$conf['mail']['smtp'] = array(); -$conf['mail']['smtp']['host']="mail.yourdomain.example"; -$conf['mail']['smtp']['host']="locahost"; -$conf['mail']['smtp']['port']="25"; -$conf['mail']['smtp']['auth_username']="your.user@something.example"; -$conf['mail']['smtp']['auth_password']="notsecret"; -$conf['mail']['smtp']['timeout']="45"; -$conf['mail']['smtp']['localhost']='0'; -$conf['mail']['smtp']['localhost']="your.fully.qualified.hostname.example"; -$conf['mail']['smtp']['tls']=false; -$conf['mail']['smtp']['ssl']=false; - -$conf['mime-types'] = array(); -$conf['mime-types']['ez'] = 'application/andrew-inset'; -$conf['mime-types']['csm'] = 'application/cu-seeme'; -$conf['mime-types']['cu'] = 'application/cu-seeme'; -$conf['mime-types']['tsp'] = 'application/dsptype'; -$conf['mime-types']['spl'] = 'application/futuresplash'; -$conf['mime-types']['cpt'] = 'application/mac-compactpro'; -$conf['mime-types']['hqx'] = 'application/mac-binhex40'; -$conf['mime-types']['nb'] = 'application/mathematica'; -$conf['mime-types']['mdb'] = 'application/msaccess'; -$conf['mime-types']['doc'] = 'application/msword'; -$conf['mime-types']['dot'] = 'application/msword'; -$conf['mime-types']['bin'] = 'application/octet-stream'; -$conf['mime-types']['oda'] = 'application/oda'; -$conf['mime-types']['pdf'] = 'application/pdf'; -$conf['mime-types']['pgp'] = 'application/pgp-signature'; -$conf['mime-types']['ps'] = 'application/postscript'; -$conf['mime-types']['ai'] = 'application/postscript'; -$conf['mime-types']['eps'] = 'application/postscript'; -$conf['mime-types']['rtf'] = 'application/rtf'; -$conf['mime-types']['smi'] = 'application/smil'; -$conf['mime-types']['smil'] = 'application/smil'; -$conf['mime-types']['xls'] = 'application/vnd.ms-excel'; -$conf['mime-types']['xlb'] = 'application/vnd.ms-excel'; -$conf['mime-types']['ppt'] = 'application/vnd.ms-powerpoint'; -$conf['mime-types']['pps'] = 'application/vnd.ms-powerpoint'; -$conf['mime-types']['pot'] = 'application/vnd.ms-powerpoint'; -$conf['mime-types']['sdw'] = 'application/vnd.stardivision.writer'; -$conf['mime-types']['sgl'] = 'application/vnd.stardivision.writer-global'; -$conf['mime-types']['vor'] = 'application/vnd.stardivision.writer'; -$conf['mime-types']['sdc'] = 'application/vnd.stardivision.calc'; -$conf['mime-types']['sda'] = 'application/vnd.stardivision.draw'; -$conf['mime-types']['sdd'] = 'application/vnd.stardivision.impress'; -$conf['mime-types']['sdp'] = 'application/vnd.stardivision.impress-packed'; -$conf['mime-types']['smf'] = 'application/vnd.stardivision.math'; -$conf['mime-types']['sds'] = 'application/vnd.stardivision.chart'; -$conf['mime-types']['smd'] = 'application/vnd.stardivision.mail'; -$conf['mime-types']['wbxml'] = 'application/vnd.wap.wbxml '; -$conf['mime-types']['wmlc'] = 'application/vnd.wap.wmlc'; -$conf['mime-types']['wmlsc'] = 'application/vnd.wap.wmlscriptc'; -$conf['mime-types']['wp5'] = 'application/wordperfect5.1'; -$conf['mime-types']['zip'] = 'application/zip'; -$conf['mime-types']['wk'] = 'application/x-123'; -$conf['mime-types']['bcpio'] = 'application/x-bcpio'; -$conf['mime-types']['vcd'] = 'application/x-cdlink '; -$conf['mime-types']['pgn'] = 'application/x-chess-pgn'; -$conf['mime-types']['cpio'] = 'application/x-cpio'; -$conf['mime-types']['csh'] = 'application/x-csh'; -$conf['mime-types']['deb'] = 'application/x-debian-package'; -$conf['mime-types']['dcr'] = 'application/x-director'; -$conf['mime-types']['dir'] = 'application/x-director'; -$conf['mime-types']['dxr'] = 'application/x-director'; -$conf['mime-types']['wad'] = 'application/x-doom'; -$conf['mime-types']['dms'] = 'application/x-dms'; -$conf['mime-types']['dvi'] = 'application/x-dvi'; -$conf['mime-types']['pfa'] = 'application/x-font'; -$conf['mime-types']['pfb'] = 'application/x-font'; -$conf['mime-types']['gsf'] = 'application/x-font'; -$conf['mime-types']['pcf'] = 'application/x-font'; -$conf['mime-types']['spl'] = 'application/x-futuresplash '; -$conf['mime-types']['gnumeric'] = 'application/x-gnumeric'; -$conf['mime-types']['gtar'] = 'application/x-gtar'; -$conf['mime-types']['tgz'] = 'application/x-gtar'; -$conf['mime-types']['taz'] = 'application/x-gtar'; -$conf['mime-types']['hdf'] = 'application/x-hdf'; -$conf['mime-types']['phtml'] = 'text/html'; -$conf['mime-types']['pht'] = 'text/html'; -$conf['mime-types']['php'] = 'text/html'; -$conf['mime-types']['phps'] = 'text/html'; -$conf['mime-types']['php3'] = 'text/html'; -$conf['mime-types']['php3p'] = 'text/html '; -$conf['mime-types']['php4'] = 'text/html'; -$conf['mime-types']['docbook'] = 'application/docbook+xml'; -$conf['mime-types']['ica'] = 'application/x-ica'; -$conf['mime-types']['jar'] = 'application/x-java-archive'; -$conf['mime-types']['jnlp'] = 'application/x-java-jnlp-file'; -$conf['mime-types']['ser'] = 'application/x-java-serialized-object'; -$conf['mime-types']['class'] = 'application/x-java-vm'; -$conf['mime-types']['js'] = 'application/x-javascript'; -$conf['mime-types']['chrt'] = 'application/x-kchart'; -$conf['mime-types']['kil'] = 'application/x-killustrator'; -$conf['mime-types']['kpr'] = 'application/x-kpresenter'; -$conf['mime-types']['kpt'] = 'application/x-kpresenter'; -$conf['mime-types']['skp'] = 'application/x-koan '; -$conf['mime-types']['skd'] = 'application/x-koan '; -$conf['mime-types']['skt'] = 'application/x-koan '; -$conf['mime-types']['skm'] = 'application/x-koan '; -$conf['mime-types']['ksp'] = 'application/x-kspread'; -$conf['mime-types']['kwd'] = 'application/x-kword'; -$conf['mime-types'][' kwt'] = 'application/x-kword'; -$conf['mime-types']['latex'] = 'application/x-latex'; -$conf['mime-types']['lha'] = 'application/x-lha'; -$conf['mime-types']['lzh'] = 'application/x-lzh'; -$conf['mime-types']['lzx'] = 'application/x-lzx'; -$conf['mime-types']['frm'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['maker'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['frame'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['fm'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['fb'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['book'] = 'fbdocapplication/x-maker'; -$conf['mime-types']['mif'] = 'application/x-mif'; -$conf['mime-types']['com'] = 'application/x-msdos-program'; -$conf['mime-types']['exe'] = 'application/x-msdos-program'; -$conf['mime-types']['bat'] = 'application/x-msdos-program'; -$conf['mime-types']['dll'] = 'application/x-msdos-program'; -$conf['mime-types']['msi'] = 'application/x-msi'; -$conf['mime-types']['nc'] = 'application/x-netcdf'; -$conf['mime-types']['cdf'] = 'application/x-netcdf'; -$conf['mime-types']['pac'] = 'application/x-ns-proxy-autoconfig'; -$conf['mime-types']['o'] = 'application/x-object'; -$conf['mime-types']['ogg'] = 'application/x-ogg'; -$conf['mime-types']['oza'] = 'application/x-oz-application'; -$conf['mime-types']['pl'] = 'application/x-perl'; -$conf['mime-types']['pm'] = 'application/x-perl'; -$conf['mime-types']['crl'] = 'application/x-pkcs7-crl'; -$conf['mime-types']['rpm'] = 'application/x-redhat-package-manager'; -$conf['mime-types']['shar'] = 'application/x-shar'; -$conf['mime-types']['swf'] = 'application/x-shockwave-flash'; -$conf['mime-types']['swfl'] = 'application/x-shockwave-flash'; -$conf['mime-types']['sh'] = 'application/x-sh '; -$conf['mime-types']['sit'] = 'application/x-stuffit'; -$conf['mime-types']['sv4cpio'] = 'application/x-sv4cpio'; -$conf['mime-types']['sv4crc'] = 'application/x-sv4crc'; -$conf['mime-types']['tar'] = 'application/x-tar'; -$conf['mime-types']['tcl'] = 'application/x-tcl'; -$conf['mime-types']['tex'] = 'application/x-tex'; -$conf['mime-types']['gf'] = 'application/x-tex-gf'; -$conf['mime-types']['pk'] = 'application/x-tex-pk'; -$conf['mime-types']['texinfo'] = 'application/x-texinfo'; -$conf['mime-types']['texi'] = 'application/x-texinfo'; -$conf['mime-types']['; "~"'] = 'application/x-trash'; -$conf['mime-types'][';"%"'] = 'application/x-trash'; -$conf['mime-types']['bak'] = 'application/x-trash'; -$conf['mime-types']['old'] = 'application/x-trash'; -$conf['mime-types']['sik'] = 'application/x-trash'; -$conf['mime-types']['t'] = 'application/x-troff'; -$conf['mime-types']['tr'] = 'application/x-troff'; -$conf['mime-types']['roff'] = 'application/x-troff'; -$conf['mime-types']['man'] = 'application/x-troff-man'; -$conf['mime-types']['me'] = 'application/x-troff-me'; -$conf['mime-types']['ms'] = 'application/x-troff-ms'; -$conf['mime-types']['ustar'] = 'application/x-ustar'; -$conf['mime-types']['src'] = 'application/x-wais-source'; -$conf['mime-types']['wz'] = 'application/x-wingz'; -$conf['mime-types']['crt'] = 'application/x-x509-ca-cert'; -$conf['mime-types']['fig'] = 'application/x-xfig'; -$conf['mime-types']['au'] = 'audio/basic'; -$conf['mime-types']['snd'] = 'audio/basic'; -$conf['mime-types']['mid'] = 'audio/midi'; -$conf['mime-types']['midi'] = 'audio/midi'; -$conf['mime-types']['kar'] = 'audio/midi'; -$conf['mime-types']['mpga'] = 'audio/mpeg'; -$conf['mime-types']['mpega'] = 'audio/mpeg'; -$conf['mime-types']['mp2'] = 'audio/mpeg'; -$conf['mime-types']['mp3'] = 'audio/mpeg'; -$conf['mime-types']['m3u'] = 'audio/mpegurl'; -$conf['mime-types']['sid'] = 'audio/prs.sid'; -$conf['mime-types']['aif'] = 'audio/x-aiff'; -$conf['mime-types']['aiff'] = 'audio/x-aiff'; -$conf['mime-types']['aifc'] = 'audio/x-aiff'; -$conf['mime-types']['gsm'] = 'audio/x-gsm'; -$conf['mime-types']['m3u'] = 'audio/x-mpegurl'; -$conf['mime-types']['rpm'] = 'audio/x-pn-realaudio-plugin '; -$conf['mime-types']['ra'] = 'audio/x-pn-realaudio'; -$conf['mime-types']['rm'] = 'audio/x-pn-realaudio'; -$conf['mime-types']['ram'] = 'audio/x-pn-realaudio'; -$conf['mime-types']['ra'] = 'audio/x-realaudio '; -$conf['mime-types']['pls'] = 'audio/x-scpls'; -$conf['mime-types']['wav'] = 'audio/x-wav'; -$conf['mime-types']['pdb'] = 'chemical/x-pdb'; -$conf['mime-types']['xyz'] = 'chemical/x-xyz '; -$conf['mime-types']['bmp'] = 'image/bmp'; -$conf['mime-types']['gif'] = 'image/gif'; -$conf['mime-types']['ief'] = 'image/ief'; -$conf['mime-types']['jpeg'] = 'image/jpeg'; -$conf['mime-types']['jpg'] = 'image/jpeg'; -$conf['mime-types']['jpe'] = 'image/jpeg'; -$conf['mime-types']['pcx'] = 'image/pcx'; -$conf['mime-types']['png'] = 'image/png'; -$conf['mime-types']['svg'] = 'image/svg+xml'; -$conf['mime-types']['svgz'] = 'image/svg+xml'; -$conf['mime-types']['tiff'] = 'image/tiff'; -$conf['mime-types']['tif'] = 'image/tiff'; -$conf['mime-types']['wbmp'] = 'image/vnd.wap.wbmp'; -$conf['mime-types']['ras'] = 'image/x-cmu-raster'; -$conf['mime-types']['cdr'] = 'image/x-coreldraw'; -$conf['mime-types']['pat'] = 'image/x-coreldrawpattern'; -$conf['mime-types']['cdt'] = 'image/x-coreldrawtemplate'; -$conf['mime-types']['cpt'] = 'image/x-corelphotopaint'; -$conf['mime-types']['djvu'] = 'image/x-djvu'; -$conf['mime-types']['djv'] = 'image/x-djvu'; -$conf['mime-types']['jng'] = 'image/x-jng'; -$conf['mime-types']['bmp'] = 'image/x-ms-bmp'; -$conf['mime-types']['pnm'] = 'image/x-portable-anymap'; -$conf['mime-types']['pbm'] = 'image/x-portable-bitmap'; -$conf['mime-types']['pgm'] = 'image/x-portable-graymap'; -$conf['mime-types']['ppm'] = 'image/x-portable-pixmap'; -$conf['mime-types']['rgb'] = 'image/x-rgb'; -$conf['mime-types']['xbm'] = 'image/x-xbitmap'; -$conf['mime-types']['xpm'] = 'image/x-xpixmap'; -$conf['mime-types']['xwd'] = 'image/x-xwindowdump'; -$conf['mime-types']['igs'] = 'model/iges'; -$conf['mime-types']['iges'] = 'model/iges'; -$conf['mime-types']['msh'] = 'model/mesh'; -$conf['mime-types']['mesh'] = 'model/mesh'; -$conf['mime-types']['silo'] = 'model/mesh'; -$conf['mime-types']['wrl'] = 'model/vrml'; -$conf['mime-types']['vrml'] = 'model/vrml'; -$conf['mime-types']['csv'] = 'text/comma-separated-values'; -$conf['mime-types']['css'] = 'text/css'; -$conf['mime-types']['htm'] = 'text/html'; -$conf['mime-types']['html'] = 'text/html'; -$conf['mime-types']['xhtml'] = 'text/html'; -$conf['mime-types']['mml'] = 'text/mathml'; -$conf['mime-types']['asc'] = 'text/plain'; -$conf['mime-types']['txt'] = 'text/plain'; -$conf['mime-types']['text'] = 'text/plain'; -$conf['mime-types']['diff'] = 'text/plain'; -$conf['mime-types']['rtx'] = 'text/richtext'; -$conf['mime-types']['rtf'] = 'text/rtf'; -$conf['mime-types']['tsv'] = 'text/tab-separated-values'; -$conf['mime-types']['wml'] = 'text/vnd.wap.wml'; -$conf['mime-types']['wmls'] = 'text/vnd.wap.wmlscript'; -$conf['mime-types']['xml'] = 'text/xml'; -$conf['mime-types']['xsl'] = 'text/xml'; -$conf['mime-types']['hpp'] = 'text/x-c++hdr'; -$conf['mime-types']['hxx'] = 'text/x-c++hdr'; -$conf['mime-types']['hh'] = 'text/x-c++hdr'; -$conf['mime-types']['cpp'] = 'text/x-c++src'; -$conf['mime-types']['cxx'] = 'text/x-c++src'; -$conf['mime-types']['cc'] = 'text/x-c++src'; -$conf['mime-types']['h'] = 'text/x-chdr'; -$conf['mime-types']['csh'] = 'text/x-csh'; -$conf['mime-types']['c'] = 'text/x-csrc'; -$conf['mime-types']['java'] = 'text/x-java'; -$conf['mime-types']['moc'] = 'text/x-moc'; -$conf['mime-types']['p'] = 'text/x-pascal'; -$conf['mime-types']['pas'] = 'text/x-pascal'; -$conf['mime-types']['etx'] = 'text/x-setext'; -$conf['mime-types']['sh'] = 'text/x-sh'; -$conf['mime-types']['tcl'] = 'text/x-tcl'; -$conf['mime-types']['tk'] = 'text/x-tcl'; -$conf['mime-types']['tex'] = 'text/x-tex'; -$conf['mime-types']['ltx'] = 'text/x-tex'; -$conf['mime-types']['sty'] = 'text/x-tex'; -$conf['mime-types']['cls'] = 'text/x-tex'; -$conf['mime-types']['vcs'] = 'text/x-vcalendar'; -$conf['mime-types']['vcf'] = 'text/x-vcard'; -$conf['mime-types']['dl'] = 'video/dl'; -$conf['mime-types']['fli'] = 'video/fli'; -$conf['mime-types']['gl'] = 'video/gl'; -$conf['mime-types']['mpeg'] = 'video/mpeg'; -$conf['mime-types']['mpg'] = 'video/mpeg'; -$conf['mime-types']['mpe'] = 'video/mpeg'; -$conf['mime-types']['qt'] = 'video/quicktime'; -$conf['mime-types']['mov'] = 'video/quicktime'; -$conf['mime-types']['mxu'] = 'video/vnd.mpegurl'; -$conf['mime-types']['mng'] = 'video/x-mng'; -$conf['mime-types']['asf'] = 'video/x-ms-asf'; -$conf['mime-types']['asx'] = 'video/x-ms-asf'; -$conf['mime-types']['avi'] = 'video/x-msvideo'; -$conf['mime-types']['movie'] = 'video/x-sgi-movie'; -$conf['mime-types']['ice'] = 'x-conference/x-cooltalk'; -$conf['mime-types']['vrm'] = 'x-world/x-vrml'; -$conf['mime-types']['vrml'] = 'x-world/x-vrml'; -$conf['mime-types']['wrl'] = 'x-world/x-vrml'; - -$conf['publish'] = array(); -$conf['publish']['edit']=true; -$conf['publish']['default']='index'; -$conf['publish']['format']= "{filename}{language_sep}{language}{type_sep}{type}"; -$conf['publish']['language_sep']= "."; -$conf['publish']['type_sep']= "."; -$conf['publish']['filename_language']='auto'; -$conf['publish']['filename_type']='always'; -$conf['publish']['style']="id"; -$conf['publish']['url']='relative'; -$conf['publish']['url']='absolute'; -$conf['publish']['enable_php_in_page_content']=false; -$conf['publish']['enable_php_in_file_content']=false; -$conf['publish']['escape_8bit_characters']=false; -$conf['publish']['encode_utf8_in_html']=true; -$conf['publish']['negotiation'] = array(); -$conf['publish']['negotiation']['page_negotiate_type']=true; -$conf['publish']['negotiation']['page_negotiate_language']=true; -$conf['publish']['negotiation']['file_negotiate_type']=true; -$conf['publish']['filesystem'] = array(); -$conf['publish']['filesystem']['per_project']=true; -$conf['publish']['filesystem']['directory']='/var/www/'; -$conf['publish']['command'] = array(); -$conf['publish']['command']['per_project']=true; -$conf['publish']['command']['enable']=false; -$conf['publish']['command']['command']=''; -$conf['publish']['ftp'] = array(); -$conf['publish']['ftp']['enable']=true; -$conf['publish']['ftp']['per_project']=true; -$conf['publish']['ftp']['port']='21'; -$conf['publish']['ftp']['host']=''; -$conf['publish']['ftp']['path']=''; -$conf['publish']['ftp']['user']='anonymous'; -$conf['publish']['ftp']['pass']='mail@example.com'; -$conf['replace'] = array(); -$conf['replace']['']='0'; -$conf['replace']['']='0'; -$conf['replace']['euro']= "EUR,&euro;"; -$conf['replace']['copy']= "(c),&copy;"; -$conf['search'] = array(); -$conf['search']['']='0'; -$conf['search']['quicksearch'] = array(); -$conf['search']['quicksearch']['flag'] = array(); -$conf['search']['quicksearch']['flag']['id']=true; -$conf['search']['quicksearch']['flag']['name']=true; -$conf['search']['quicksearch']['flag']['filename']=true; -$conf['search']['quicksearch']['flag']['description']=true; -$conf['search']['quicksearch']['flag']['content']=false; -$conf['security'] = array(); -$conf['security']['readonly']=false; -$conf['security']['nopublish']=false; -$conf['security']['umask']='0'; -$conf['security']['chmod']='0'; -$conf['security']['chmod_dir']='0'; -$conf['security']['']='0'; -$conf['security']['disable_dynamic_code']=true; -$conf['security']['show_system_info']=true; -$conf['security']['use_post_token']=true; -$conf['security']['renew_session_login']=false; -$conf['security']['renew_session_logout']=false; -$conf['security']['default'] = array(); -$conf['security']['default']['username']=''; -$conf['security']['default']['password']=''; -$conf['security']['guest'] = array(); -$conf['security']['guest']['enable']=false; -$conf['security']['guest']['user']='guest'; -$conf['security']['login'] = array(); -$conf['security']['login']['type']='form'; -$conf['security']['auth'] = array(); -$conf['security']['auth']['type']='database'; -$conf['security']['auth']['userdn']=false; -$conf['security']['authorize'] = array(); -$conf['security']['authorize']['type']='database'; -$conf['security']['authorize']['type']='ldap'; - -$conf['security']['modules'] = array(); -$conf['security']['modules']['autologin']='Remember,Guest,SingleSignon'; -$conf['security']['modules']['preselect']='Ident,SSL,Cookie'; -$conf['security']['modules']['authenticate']='LdapUserDN,Database,Internal'; - -$conf['security']['newuser'] = array(); -$conf['security']['newuser']['autoadd'] = true; -$conf['security']['newuser']['autogroups'] = ""; - -$conf['security']['password'] = array(); -$conf['security']['password']['random_length']=10; -$conf['security']['password']['min_length']=6; -$conf['security']['password']['pepper']= ''; -$conf['security']['password']['deny_after_expiration_duration'] = 72; -$conf['security']['password']['force_change_if_cleartext']= true; -$conf['security']['http'] = array(); -$conf['security']['http']['url']= "http://example.net/restricted-area"; -$conf['security']['authdb'] = array(); -$conf['security']['authdb']['enable'] = false; -$conf['security']['authdb']['type']='postgresql'; -$conf['security']['authdb']['user']='dbuser'; -$conf['security']['authdb']['password']='dbpassword'; -$conf['security']['authdb']['host']= '127.0.0.1'; -$conf['security']['authdb']['database']='dbname'; -$conf['security']['authdb']['persistent']=false; -$conf['security']['authdb']['prepare']=false; -$conf['security']['authdb']['sql']= "select 1 from table where user={username} and password={password}"; -$conf['security']['authdb']['hash_algo']='md5'; -$conf['security']['authdb']['add']=true; -$conf['security']['ssl'] = array(); -$conf['security']['ssl']['trust']=false; -$conf['security']['ssl']['client_cert_dn_env'] = 'SSL_CLIENT_S_DN_CN'; -$conf['security']['openid'] = array(); -$conf['security']['openid']['enable']=false; -$conf['security']['openid']['add']=false; -$conf['security']['openid']['logo_url']='0'; -$conf['security']['openid']['logo_url']="http://openid.net/login-bg.gif"; -$conf['security']['openid']['trust_root']='http://your.server.example/openrat/'; -$conf['security']['openid']['trust_root']='0'; -$conf['security']['openid']['trusted_server']='openid1.example.com,openid2.example.com'; -$conf['security']['openid']['trusted_server']='0'; -$conf['security']['openid']['update_user']=true; -$conf['security']['openid']['user_identity']=true; -$conf['security']['openid']['provider']['name']='google'; -$conf['security']['openid']['provider']['google']['xrds_uri']="http://google.com/accounts/o8/id"; -$conf['security']['openid']['provider']['google']['map_attribute']="email"; -$conf['security']['openid']['provider']['google']['name']="Google"; -$conf['security']['openid']['provider']['google']['map_internal']="mail"; -$conf['security']['openid']['provider']['yahoo']['xrds_uri']="http://??????"; -$conf['security']['openid']['provider']['yahoo']['map_attribute']="usename"; -$conf['security']['openid']['provider']['yahoo']['map_internal']="mail"; -$conf['security']['sso'] = array(); -$conf['security']['sso']['enable']=false; -$conf['security']['sso']['url']="http://localhost/check.php?phpsessid={id}&check=true"; -$conf['security']['sso']['url']="https://www.example.com/phpmyadmin/main.php?server=1"; -$conf['security']['sso']['auth_param_name']='authid'; -$conf['security']['sso']['auth_param_serialized']=true; -$conf['security']['sso']['cookie']=true; -$conf['security']['sso']['cookie_name']='0'; -$conf['security']['sso']['force']=true; -$conf['security']['sso']['expect']='0'; -$conf['security']['sso']['expect_regexp']="/running on/"; -$conf['security']['sso']['username_regexp']="/running on localhost as ([a-z]+)@localhost/"; -$conf['security']['logout'] = array(); -$conf['security']['logout']['redirect_url']="http://your.intranet.example/"; -$conf['security']['logout']['redirect_url']='0'; -$conf['security']['user'] = array(); -$conf['security']['user']['show_admin_mail']=true; -$conf['security']['user']['show_mail']=true; -$conf['security']['user']['send_message']=true; -$conf['security']['content-security-policy']=true; - -$conf['style-default'] = array(); -$conf['style-default']['name']='Unnamed'; -$conf['style-default']['title_background_color']='grey'; -$conf['style-default']['title_text_color']='white'; -$conf['style-default']['text_color' ]= 'black'; -$conf['style-default']['background_color' ]= '#d9d9d9'; -$conf['style-default']['inactive_background_color' ]= 'silver'; - - -$conf['style'] = array(); -$conf['style']['earlgrey']=array(); -$conf['style']['earlgrey']['name']='Earl grey'; -$conf['style']['earlgrey']['title_background_color']='grey'; -$conf['style']['earlgrey']['title_text_color']='white'; -$conf['style']['earlgrey']['text_color'] ='black'; -$conf['style']['earlgrey']['background_color'] = '#e9e9e9'; -$conf['style']['earlgrey']['inactive_background_color'] = 'silver'; - -// $conf['style']['system']=array(); -// $conf['style']['system']['name']='System colors'; -// $conf['style']['system']['title_background_color']='Menu'; -// $conf['style']['system']['title_text_color']='MenuText'; -// $conf['style']['system']['text_color'] ='WindowText'; -// $conf['style']['system']['background_color'] = 'Background'; -// $conf['style']['system']['inactive_background_color'] = 'WindowFrame'; - -$conf['style']['modern']=array(); -$conf['style']['modern']['name']='Blue sky'; -$conf['style']['modern']['title_background_color']='#3F6194'; -$conf['style']['modern']['title_text_color']='white'; -$conf['style']['modern']['text_color'] ='black'; -$conf['style']['modern']['background_color'] = '#F3F3F3'; -$conf['style']['modern']['inactive_background_color'] = '#CCCCCC'; - -$conf['style']['moorweide']=array(); -$conf['style']['moorweide']['name']='Moorweide'; -$conf['style']['moorweide']['title_background_color']='#006633'; -$conf['style']['moorweide']['title_text_color']='white'; -$conf['style']['moorweide']['text_color'] ='black'; -$conf['style']['moorweide']['background_color'] = '#F5FFFA'; -$conf['style']['moorweide']['inactive_background_color'] = '#CEE6DA'; - -$conf['style']['dark']=array(); -$conf['style']['dark']['name']='Dark'; -$conf['style']['dark']['title_background_color']='#868685'; -$conf['style']['dark']['title_text_color']='#DCDCDC'; -$conf['style']['dark']['text_color'] ='#FFFFFF'; -$conf['style']['dark']['background_color'] = '#201F1D'; -$conf['style']['dark']['inactive_background_color'] = '#868685'; - -$conf['theme'] = array(); -$conf['theme']['compiler'] = array(); -$conf['theme']['compiler']['enable']=true; -$conf['theme']['compiler']['cache']=true; -$conf['theme']['compiler']['chmod']=''; -$conf['theme']['compiler']['compile_at_logout']=false; -$conf['wiki'] = array(); -$conf['wiki']['convert_html']=true; -$conf['wiki']['convert_bbcode']=true; -$conf['wiki']['tag_strong']= "*"; -$conf['wiki']['tag_emphatic']= "_"; - -$conf['application']['name' ] = OR_TITLE; -$conf['application']['version' ] = OR_VERSION; -$conf['application']['operator'] = OR_TITLE; -$conf['production']= true; - -?>- \ No newline at end of file diff --git a/util/exception/OpenRatException.class.php b/util/exception/OpenRatException.class.php @@ -1,25 +0,0 @@ -<?php - -class OpenRatException extends Exception -{ - public $key; - - // Die Exception neu definieren, damit die Mitteilung nicht optional ist - public function __construct($key, $message, $code = 0, Exception $previous = null) { - - $this->key = $key; - - // sicherstellen, dass alles korrekt zugewiesen wird - parent::__construct($message, $code, $previous); - } - - // maßgeschneiderte Stringdarstellung des Objektes - public function __toString() { - return __CLASS__ . ": ".$this->key." [{$this->code}]: '{$this->message}' in {$this->file}({$this->line})\n" - . "{$this->getTraceAsString()}\n"; - } - -} - - -?>- \ No newline at end of file diff --git a/util/exception/SecurityException.class.php b/util/exception/SecurityException.class.php @@ -1,9 +0,0 @@ -<?php - -class SecurityException extends RuntimeException -{ - -} - - -?>- \ No newline at end of file diff --git a/util/include.inc.php b/util/include.inc.php @@ -1,65 +0,0 @@ -<?php - -require_once( OR_SERVICECLASSES_DIR."GlobalFunctions.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Http.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Html.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Text.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Mail.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Ldap.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."FileUtils.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Less.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."JSqueeze.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."Spyc.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."exception/OpenRatException.class.".PHP_EXT ); -require_once( OR_SERVICECLASSES_DIR."exception/SecurityException.class.".PHP_EXT ); - - - -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('tree')) ) -{ - require_once( OR_SERVICECLASSES_DIR."TreeElement.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."AbstractTree.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."AdministrationTree.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."ProjectTree.class.".PHP_EXT ); -} - -// Veroeffentlichung -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','page','pageelement','folder')) ) -{ - require_once( OR_SERVICECLASSES_DIR."Publish.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."Ftp.class.".PHP_EXT ); -} - -// Nur bei der Erzeugung von Seiten notwendig. -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('pageelement','page','folder','element')) ) -{ - require_once( OR_SERVICECLASSES_DIR."Macro.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."Dynamic.class.".PHP_EXT ); -} - -// Nur bei der Erzeugung von Seiten notwendig. -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('pageelement','page','folder')) ) -{ - require_once( OR_SERVICECLASSES_DIR."Api.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."Code.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."Transformer.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."Line.class.".PHP_EXT ); -} - - -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','folder','filebrowser')) ) -{ - require_once( OR_SERVICECLASSES_DIR."Upload.class.".PHP_EXT ); -} - - -//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','folder')) ) -{ - require_once( OR_SERVICECLASSES_DIR."Upload.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."ArchiveTar.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."ArchiveUnzip.class.".PHP_EXT ); - require_once( OR_SERVICECLASSES_DIR."ArchiveZip.class.".PHP_EXT ); -} - -?>- \ No newline at end of file