diff --git a/build/docker/Dockerfile b/build/docker/Dockerfile index 4b1df7e0876..9264eed5ef2 100644 --- a/build/docker/Dockerfile +++ b/build/docker/Dockerfile @@ -3,7 +3,7 @@ FROM php:7.2-apache ENV HOST_USER_ID 33 ENV PHP_INI_DATE_TIMEZONE 'UTC' -RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev libldap2-dev libzip-dev zlib1g-dev libicu-dev g++\ +RUN apt-get update && apt-get install -y libpng16-16 libpng-dev libjpeg62-turbo libjpeg62-turbo-dev libldap2-dev zlib1g-dev libicu-dev g++\ && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-configure gd --with-png-dir=/usr --with-jpeg-dir=/usr \ && docker-php-ext-install gd \ @@ -14,7 +14,7 @@ RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev libldap2-dev lib && docker-php-ext-install calendar \ && docker-php-ext-configure intl \ && docker-php-ext-install intl \ - && apt-get autoremove --purge -y libjpeg-dev libldap2-dev zlib1g-dev libicu-dev g++ + && apt-get autoremove --purge -y libpng-dev libjpeg62-turbo-dev libldap2-dev zlib1g-dev libicu-dev g++ RUN mkdir /var/documents RUN chown www-data /var/documents diff --git a/build/docker/docker-compose.yml b/build/docker/docker-compose.yml index 3fe6125a874..cc839810e7f 100644 --- a/build/docker/docker-compose.yml +++ b/build/docker/docker-compose.yml @@ -21,5 +21,12 @@ web: - ../../htdocs:/var/www/html links: - mariadb + - mail ports: - "80:80" + +mail: + image: maildev/maildev + ports: + - "8081:80" + - "25:25" \ No newline at end of file diff --git a/htdocs/bom/bom_card.php b/htdocs/bom/bom_card.php index 5d072036851..202373dd302 100644 --- a/htdocs/bom/bom_card.php +++ b/htdocs/bom/bom_card.php @@ -519,7 +519,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea $keyforbreak = 'duration'; include DOL_DOCUMENT_ROOT.'/core/tpl/commonfields_view.tpl.php'; - print ''.$langs->trans("TotalCost").''.price($object->total_cost).''; + print ''.$form->textwithpicto($langs->trans("TotalCost"), $langs->trans("BOMTotalCost")).''.price($object->total_cost).''; print ''.$langs->trans("UnitCost").''.price($object->unit_cost).''; // Other attributes diff --git a/htdocs/bom/class/bom.class.php b/htdocs/bom/class/bom.class.php index 8d6927c10cf..57ce682a418 100644 --- a/htdocs/bom/class/bom.class.php +++ b/htdocs/bom/class/bom.class.php @@ -997,11 +997,20 @@ class BOM extends CommonObject $this->unit_cost = 0; $this->total_cost = 0; + require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; + $productFournisseur = new ProductFournisseur($this->db); + foreach ($this->lines as &$line) { $tmpproduct = new Product($this->db); $tmpproduct->fetch($line->fk_product); + $line->unit_cost = price2num((!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp); + if (empty($line->unit_cost)) { + if ($productFournisseur->find_min_price_product_fournisseur($line->fk_product) > 0) + { + $line->unit_cost = $productFournisseur->fourn_unitprice; + } + } - $line->unit_cost = (!empty($tmpproduct->cost_price)) ? $tmpproduct->cost_price : $tmpproduct->pmp; // TODO : add option to work with cost_price or pmp $line->total_cost = price2num($line->qty * $line->unit_cost, 'MT'); $this->total_cost += $line->total_cost; } diff --git a/htdocs/comm/action/class/actioncomm.class.php b/htdocs/comm/action/class/actioncomm.class.php index 7c35b9979c7..2abc2fa5798 100644 --- a/htdocs/comm/action/class/actioncomm.class.php +++ b/htdocs/comm/action/class/actioncomm.class.php @@ -337,6 +337,20 @@ class ActionComm extends CommonObject */ public $errors_to; + /** + * Typical value for a event that is in a todo state + */ + const EVENT_TODO = 0; + + /** + * Typical value for a event that is in a progress state + */ + const EVENT_IN_PROGRESS = 50; + + /** + * Typical value for a event that is in a finished state + */ + const EVENT_FINISHED = 100; /** * Constructor @@ -1961,4 +1975,32 @@ class ActionComm extends CommonObject return $error; } + + /** + * Udpate the percent value of a event with the given id + * + * @param int $id The id of the event + * @param int $percent The new percent value for the event + * @return int 1 when update of the event was suscessfull, otherwise -1 + */ + public function updatePercent($id, $percent) + { + $this->db->begin(); + + $sql = "UPDATE ".MAIN_DB_PREFIX."actioncomm "; + $sql .= " SET percent = ".(int) $percent; + $sql .= " WHERE id=".$id; + + if ($this->db->query($sql)) + { + $this->db->commit(); + return 1; + } + else + { + $this->db->rollback(); + $this->error = $this->db->lasterror(); + return -1; + } + } } diff --git a/htdocs/comm/action/list.php b/htdocs/comm/action/list.php index 5e71e150ece..f2197c0d06d 100644 --- a/htdocs/comm/action/list.php +++ b/htdocs/comm/action/list.php @@ -5,6 +5,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2017 Open-DSI * Copyright (C) 2018 Frédéric France + * Copyright (C) 2020 Tobias Sekan * * 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 @@ -40,6 +41,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; $langs->loadLangs(array("users", "companies", "agenda", "commercial", "other")); $action = GETPOST('action', 'alpha'); +$massaction = GETPOST('massaction', 'alpha'); $contextpage = GETPOST('contextpage', 'aZ') ?GETPOST('contextpage', 'aZ') : 'actioncommlist'; // To manage different context of search $resourceid = GETPOST("search_resourceid", "int") ?GETPOST("search_resourceid", "int") : GETPOST("resourceid", "int"); $pid = GETPOST("search_projectid", 'int', 3) ?GETPOST("search_projectid", 'int', 3) : GETPOST("projectid", 'int', 3); @@ -49,6 +51,8 @@ $optioncss = GETPOST('optioncss', 'alpha'); $year = GETPOST("year", 'int'); $month = GETPOST("month", 'int'); $day = GETPOST("day", 'int'); +$toselect = GETPOST('toselect', 'array'); + // Set actioncode (this code must be same for setting actioncode into peruser, listacton and index) if (GETPOST('search_actioncode', 'array')) { @@ -183,9 +187,42 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x' $datestart = ''; $dateend = ''; $search_status = ''; + $toselect = ''; $search_array_options = array(); } +if (empty($reshook) && !empty($massaction)) +{ + unset($percent); + + switch ($massaction) + { + case 'set_all_events_to_todo': + $percent = ActionComm::EVENT_TODO; + break; + + case 'set_all_events_to_in_progress': + $percent = ActionComm::EVENT_IN_PROGRESS; + break; + + case 'set_all_events_to_finished': + $percent = ActionComm::EVENT_FINISHED; + break; + } + + if(isset($percent)) + { + foreach ($toselect as $toselectid) + { + $result = $object->updatePercent($toselectid, $percent); + if($result < 0) + { + dol_print_error($db); + break; + } + } + } +} /* * View @@ -237,6 +274,15 @@ if ($optioncss != '') $param .= '&optioncss='.urlencode($optioncss); // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; +// List of mass actions available +$arrayofmassactions = array( + 'set_all_events_to_todo' => $langs->trans("SetAllEventsToTodo"), + 'set_all_events_to_in_progress' => $langs->trans("SetAllEventsToInProgress"), + 'set_all_events_to_finished' => $langs->trans("SetAllEventsToFinished"), +); + +$massactionbutton = $form->selectMassAction('', $arrayofmassactions); + $sql = "SELECT"; if ($usergroup > 0) $sql .= " DISTINCT"; $sql .= " s.nom as societe, s.rowid as socid, s.client, s.email as socemail,"; @@ -358,6 +404,8 @@ if ($resql) $num = $db->num_rows($resql); + $arrayofselected = is_array($toselect) ? $toselect : array(); + // Local calendar $newtitle = '
'.$langs->trans("LocalAgenda").'  
'; //$newtitle=$langs->trans($title); @@ -427,7 +475,7 @@ if ($resql) $newcardbutton .= dolGetButtonTitle($langs->trans('AddAction'), '', 'fa fa-plus-circle', DOL_URL_ROOT.'/comm/action/card.php?action=create&datep='.sprintf("%04d%02d%02d", $tmpforcreatebutton['year'], $tmpforcreatebutton['mon'], $tmpforcreatebutton['mday']).$hourminsec.'&backtopage='.urlencode($_SERVER["PHP_SELF"].($newparam ? '?'.$newparam : ''))); } - print_barre_liste($s, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', $num, -1 * $nbtotalofrecords, '', 0, $nav.$newcardbutton, '', $limit, 0, 0, 1); + print_barre_liste($s, $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, -1 * $nbtotalofrecords, '', 0, $nav.$newcardbutton, '', $limit, 0, 0, 1); $moreforfilter = ''; @@ -724,7 +772,15 @@ if ($resql) $datep = $db->jdate($obj->datep); print ''.$actionstatic->LibStatut($obj->percent, 5, 0, $datep).''; } - print ''; + // Action column + print ''; + if ($massactionbutton || $massaction) // If we are in select mode (massactionbutton defined) or if we have already selected and sent an action ($massaction) defined + { + $selected = 0; + if (in_array($obj->id, $arrayofselected)) $selected = 1; + print ''; + } + print ''; print "\n"; $i++; diff --git a/htdocs/compta/facture/card.php b/htdocs/compta/facture/card.php index 677701b9970..ac961e2df6a 100644 --- a/htdocs/compta/facture/card.php +++ b/htdocs/compta/facture/card.php @@ -987,7 +987,7 @@ if (empty($reshook)) // Replacement invoice if ($_POST['type'] == Facture::TYPE_REPLACEMENT) { - $dateinvoice = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $dateinvoice = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); if (empty($dateinvoice)) { $error++; @@ -1048,7 +1048,7 @@ if (empty($reshook)) $action = 'create'; } - $dateinvoice = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $dateinvoice = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); if (empty($dateinvoice)) { $error++; @@ -1229,7 +1229,7 @@ if (empty($reshook)) // Standard invoice or Deposit invoice, created from a Predefined template invoice if (($_POST['type'] == Facture::TYPE_STANDARD || $_POST['type'] == Facture::TYPE_DEPOSIT) && GETPOST('fac_rec', 'int') > 0) { - $dateinvoice = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $dateinvoice = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); if (empty($dateinvoice)) { $error++; @@ -1280,7 +1280,7 @@ if (empty($reshook)) $action = 'create'; } - $dateinvoice = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $dateinvoice = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); if (empty($dateinvoice)) { $error++; @@ -1699,7 +1699,7 @@ if (empty($reshook)) // Situation invoices if (GETPOST('type') == Facture::TYPE_SITUATION && (!empty($_POST['situations']))) { - $datefacture = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $datefacture = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); if (empty($datefacture)) { $error++; $mesg = $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Date")); @@ -2823,6 +2823,18 @@ if ($action == 'create') if (!empty($conf->multicurrency->enabled) && !empty($soc->multicurrency_code)) $currency_code = $soc->multicurrency_code; } + // when payment condition is empty (means not override by payment condition form a other object, like third-party), try to use default value + if(empty($cond_reglement_id)) + { + $cond_reglement_id = GETPOST("cond_reglement_id"); + } + + // when payment mode is empty (means not override by payment mode form a other object, like third-party), try to use default value + if(empty($mode_reglement_id)) + { + $mode_reglement_id = GETPOST("mode_reglement_id"); + } + if (!empty($soc->id)) $absolute_discount = $soc->getAvailableDiscounts(); $note_public = $object->getDefaultCreateValueFor('note_public', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($conf->global->FACTURE_REUSE_NOTES_ON_CREATE_FROM)) ? $objectsrc->note_public : null)); $note_private = $object->getDefaultCreateValueFor('note_private', ((!empty($origin) && !empty($originid) && is_object($objectsrc) && !empty($conf->global->FACTURE_REUSE_NOTES_ON_CREATE_FROM)) ? $objectsrc->note_private : null)); diff --git a/htdocs/contact/list.php b/htdocs/contact/list.php index fe80e709b28..3f7791019ec 100644 --- a/htdocs/contact/list.php +++ b/htdocs/contact/list.php @@ -354,7 +354,6 @@ if (strlen($search_fax)) $sql .= natural_search('p.fax', $search_fax) if (!empty($conf->socialnetworks->enabled)) { foreach ($socialnetworks as $key => $value) { if ($value['active'] && strlen($search_{$key})) { - //$sql.= natural_search("p.socialnetworks->'$.".$key."'", $search_{$key}); $sql .= ' AND p.socialnetworks LIKE \'%"'.$key.'":"'.$search_{$key}.'%\''; } } diff --git a/htdocs/core/antispamimage.php b/htdocs/core/antispamimage.php index 68e585c3ea0..7d6512a64a9 100644 --- a/htdocs/core/antispamimage.php +++ b/htdocs/core/antispamimage.php @@ -43,7 +43,7 @@ $number = strlen($letters); $string = ''; for ($i = 0; $i < $length; $i++) { - $string .= $letters{mt_rand(0, $number - 1)}; + $string .= $letters[mt_rand(0, $number - 1)]; } //print $string; diff --git a/htdocs/core/class/lessc.class.php b/htdocs/core/class/lessc.class.php index f5a9381b763..8ab9e58aa58 100644 --- a/htdocs/core/class/lessc.class.php +++ b/htdocs/core/class/lessc.class.php @@ -35,8 +35,7 @@ * The `lessc_formatter` takes a CSS tree, and dumps it to a formatted string, * handling things like indentation. */ -class Lessc -{ +class Lessc { public static $VERSION = "v0.5.0"; public static $TRUE = array("keyword", "true"); @@ -65,8 +64,7 @@ class Lessc protected static $nextImportId = 0; // uniquely identify imports // attempts to find the path of an import url, returns null for css files - protected function findImport($url) - { + protected function findImport($url) { foreach ((array) $this->importDir as $dir) { $full = $dir.(substr($dir, -1) != '/' ? '/' : '').$url; if ($this->fileExists($file = $full.'.less') || $this->fileExists($file = $full)) { @@ -83,24 +81,20 @@ class Lessc * @param string $name Filename * @return boolean */ - protected function fileExists($name) - { + protected function fileExists($name) { return is_file($name); } - public static function compressList($items, $delim) - { + public static function compressList($items, $delim) { if (!isset($items[1]) && isset($items[0])) return $items[0]; else return array('list', $delim, $items); } - public static function preg_quote($what) - { + public static function preg_quote($what) { return preg_quote($what, '/'); } - protected function tryImport($importPath, $parentBlock, $out) - { + protected function tryImport($importPath, $parentBlock, $out) { if ($importPath[0] == "function" && $importPath[1] == "url") { $importPath = $this->flattenList($importPath[2]); } @@ -158,8 +152,7 @@ class Lessc return array(true, $bottom, $parser, $dir); } - protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) - { + protected function compileImportedProps($props, $block, $out, $sourceParser, $importDir) { $oldSourceParser = $this->sourceParser; $oldImport = $this->importDir; @@ -197,8 +190,7 @@ class Lessc * See lessc::compileProp() * */ - protected function compileBlock($block) - { + protected function compileBlock($block) { switch ($block->type) { case "root": $this->compileRoot($block); @@ -222,8 +214,7 @@ class Lessc } } - protected function compileCSSBlock($block) - { + protected function compileCSSBlock($block) { $env = $this->pushEnv(); $selectors = $this->compileSelectors($block->tags); @@ -237,8 +228,7 @@ class Lessc $this->popEnv(); } - protected function compileMedia($media) - { + protected function compileMedia($media) { $env = $this->pushEnv($media); $parentScope = $this->mediaParent($this->scope); @@ -263,8 +253,7 @@ class Lessc $this->popEnv(); } - protected function mediaParent($scope) - { + protected function mediaParent($scope) { while (!empty($scope->parent)) { if (!empty($scope->type) && $scope->type != "media") { break; @@ -275,8 +264,7 @@ class Lessc return $scope; } - protected function compileNestedBlock($block, $selectors) - { + protected function compileNestedBlock($block, $selectors) { $this->pushEnv($block); $this->scope = $this->makeOutputBlock($block->type, $selectors); $this->scope->parent->children[] = $this->scope; @@ -287,16 +275,14 @@ class Lessc $this->popEnv(); } - protected function compileRoot($root) - { + protected function compileRoot($root) { $this->pushEnv(); $this->scope = $this->makeOutputBlock($root->type); $this->compileProps($root, $this->scope); $this->popEnv(); } - protected function compileProps($block, $out) - { + protected function compileProps($block, $out) { foreach ($this->sortProps($block->props) as $prop) { $this->compileProp($prop, $block, $out); } @@ -308,8 +294,7 @@ class Lessc * duplicate rule is detected, the comments immediately preceding each * occurence are consolidated. */ - protected function deduplicate($lines) - { + protected function deduplicate($lines) { $unique = array(); $comments = array(); @@ -327,8 +312,7 @@ class Lessc return array_merge($unique, $comments); } - protected function sortProps($props, $split = false) - { + protected function sortProps($props, $split = false) { $vars = array(); $imports = array(); $other = array(); @@ -372,8 +356,7 @@ class Lessc } } - protected function compileMediaQuery($queries) - { + protected function compileMediaQuery($queries) { $compiledQueries = array(); foreach ($queries as $query) { $parts = array(); @@ -409,36 +392,34 @@ class Lessc return $out; } - protected function multiplyMedia($env, $childQueries = null) - { + protected function multiplyMedia($env, $childQueries = null) { if (is_null($env) || !empty($env->block->type) && $env->block->type != "media" ) { return $childQueries; - } + } // plain old block, skip - if (empty($env->block->type)) { - return $this->multiplyMedia($env->parent, $childQueries); - } + if (empty($env->block->type)) { + return $this->multiplyMedia($env->parent, $childQueries); + } $out = array(); $queries = $env->block->queries; - if (is_null($childQueries)) { - $out = $queries; - } else { - foreach ($queries as $parent) { - foreach ($childQueries as $child) { - $out[] = array_merge($parent, $child); + if (is_null($childQueries)) { + $out = $queries; + } else { + foreach ($queries as $parent) { + foreach ($childQueries as $child) { + $out[] = array_merge($parent, $child); + } } } - } return $this->multiplyMedia($env->parent, $out); } - protected function expandParentSelectors(&$tag, $replace) - { + protected function expandParentSelectors(&$tag, $replace) { $parts = explode("$&$", $tag); $count = 0; foreach ($parts as &$part) { @@ -449,8 +430,7 @@ class Lessc return $count; } - protected function findClosestSelectors() - { + protected function findClosestSelectors() { $env = $this->env; $selectors = null; while ($env !== null) { @@ -466,8 +446,7 @@ class Lessc // multiply $selectors against the nearest selectors in env - protected function multiplySelectors($selectors) - { + protected function multiplySelectors($selectors) { // find parent selectors $parentSelectors = $this->findClosestSelectors(); @@ -498,8 +477,7 @@ class Lessc } // reduces selector expressions - protected function compileSelectors($selectors) - { + protected function compileSelectors($selectors) { $out = array(); foreach ($selectors as $s) { @@ -514,13 +492,11 @@ class Lessc return $out; } - protected function eq($left, $right) - { + protected function eq($left, $right) { return $left == $right; } - protected function patternMatch($block, $orderedArgs, $keywordArgs) - { + protected function patternMatch($block, $orderedArgs, $keywordArgs) { // match the guards if it has them // any one of the groups must have all its guards pass for a match if (!empty($block->guards)) { @@ -603,8 +579,7 @@ class Lessc } } - protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip = array()) - { + protected function patternMatchAll($blocks, $orderedArgs, $keywordArgs, $skip = array()) { $matches = null; foreach ($blocks as $block) { // skip seen blocks that don't have arguments @@ -621,8 +596,7 @@ class Lessc } // attempt to find blocks matched by path and args - protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen = array()) - { + protected function findBlocks($searchIn, $path, $orderedArgs, $keywordArgs, $seen = array()) { if ($searchIn == null) return null; if (isset($seen[$searchIn->id])) return null; $seen[$searchIn->id] = true; @@ -660,8 +634,7 @@ class Lessc // sets all argument names in $args to either the default value // or the one passed in through $values - protected function zipSetArgs($args, $orderedValues, $keywordValues) - { + protected function zipSetArgs($args, $orderedValues, $keywordValues) { $assignedValues = array(); $i = 0; @@ -703,8 +676,7 @@ class Lessc } // compile a prop and update $lines or $blocks appropriately - protected function compileProp($prop, $block, $out) - { + protected function compileProp($prop, $block, $out) { // set error position context $this->sourceLoc = isset($prop[-1]) ? $prop[-1] : -1; @@ -784,7 +756,7 @@ class Lessc 'list', ' ', array($subProp[2], array('keyword', $suffix)) ); - } + } $this->compileProp($subProp, $mixin, $out); } @@ -850,8 +822,7 @@ class Lessc * The input is expected to be reduced. This function will not work on * things like expressions and variables. */ - public function compileValue($value) - { + public function compileValue($value) { switch ($value[0]) { case 'list': // [1] - delimiter @@ -915,63 +886,52 @@ class Lessc } } - protected function lib_pow($args) - { + protected function lib_pow($args) { list($base, $exp) = $this->assertArgs($args, 2, "pow"); return pow($this->assertNumber($base), $this->assertNumber($exp)); } - protected function lib_pi() - { + protected function lib_pi() { return pi(); } - protected function lib_mod($args) - { + protected function lib_mod($args) { list($a, $b) = $this->assertArgs($args, 2, "mod"); return $this->assertNumber($a) % $this->assertNumber($b); } - protected function lib_tan($num) - { + protected function lib_tan($num) { return tan($this->assertNumber($num)); } - protected function lib_sin($num) - { + protected function lib_sin($num) { return sin($this->assertNumber($num)); } - protected function lib_cos($num) - { + protected function lib_cos($num) { return cos($this->assertNumber($num)); } - protected function lib_atan($num) - { + protected function lib_atan($num) { $num = atan($this->assertNumber($num)); return array("number", $num, "rad"); } - protected function lib_asin($num) - { + protected function lib_asin($num) { $num = asin($this->assertNumber($num)); return array("number", $num, "rad"); } - protected function lib_acos($num) - { + protected function lib_acos($num) { $num = acos($this->assertNumber($num)); return array("number", $num, "rad"); } - protected function lib_sqrt($num) - { + protected function lib_sqrt($num) { return sqrt($this->assertNumber($num)); } - protected function lib_extract($value) - { + protected function lib_extract($value) { list($list, $idx) = $this->assertArgs($value, 2, "extract"); $idx = $this->assertNumber($idx); // 1 indexed @@ -980,48 +940,39 @@ class Lessc } } - protected function lib_isnumber($value) - { + protected function lib_isnumber($value) { return $this->toBool($value[0] == "number"); } - protected function lib_isstring($value) - { + protected function lib_isstring($value) { return $this->toBool($value[0] == "string"); } - protected function lib_iscolor($value) - { + protected function lib_iscolor($value) { return $this->toBool($this->coerceColor($value)); } - protected function lib_iskeyword($value) - { + protected function lib_iskeyword($value) { return $this->toBool($value[0] == "keyword"); } - protected function lib_ispixel($value) - { + protected function lib_ispixel($value) { return $this->toBool($value[0] == "number" && $value[2] == "px"); } - protected function lib_ispercentage($value) - { + protected function lib_ispercentage($value) { return $this->toBool($value[0] == "number" && $value[2] == "%"); } - protected function lib_isem($value) - { + protected function lib_isem($value) { return $this->toBool($value[0] == "number" && $value[2] == "em"); } - protected function lib_isrem($value) - { + protected function lib_isrem($value) { return $this->toBool($value[0] == "number" && $value[2] == "rem"); } - protected function lib_rgbahex($color) - { + protected function lib_rgbahex($color) { $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError("color expected for rgbahex"); @@ -1035,8 +986,7 @@ class Lessc ); } - protected function lib_argb($color) - { + protected function lib_argb($color) { return $this->lib_rgbahex($color); } @@ -1046,8 +996,7 @@ class Lessc * @param array $value either an argument list (two strings) or a single string * @return string formatted url(), either as a link or base64-encoded */ - protected function lib_data_uri($value) - { + protected function lib_data_uri($value) { $mime = ($value[0] === 'list') ? $value[2][0][2] : null; $url = ($value[0] === 'list') ? $value[2][1][2][0] : $value[2][0]; @@ -1075,8 +1024,7 @@ class Lessc } // utility func to unquote a string - protected function lib_e($arg) - { + protected function lib_e($arg) { switch ($arg[0]) { case "list": $items = $arg[2]; @@ -1095,8 +1043,7 @@ class Lessc } } - protected function lib__sprintf($args) - { + protected function lib__sprintf($args) { if ($args[0] != "list") return $args; $values = $args[2]; $string = array_shift($values); @@ -1125,20 +1072,17 @@ class Lessc return array("string", $d, array($template)); } - protected function lib_floor($arg) - { + protected function lib_floor($arg) { $value = $this->assertNumber($arg); return array("number", floor($value), $arg[2]); } - protected function lib_ceil($arg) - { + protected function lib_ceil($arg) { $value = $this->assertNumber($arg); return array("number", ceil($value), $arg[2]); } - protected function lib_round($arg) - { + protected function lib_round($arg) { if ($arg[0] != "list") { $value = $this->assertNumber($arg); return array("number", round($value), $arg[2]); @@ -1149,8 +1093,7 @@ class Lessc } } - protected function lib_unit($arg) - { + protected function lib_unit($arg) { if ($arg[0] == "list") { list($number, $newUnit) = $arg[2]; return array("number", $this->assertNumber($number), @@ -1164,8 +1107,7 @@ class Lessc * Helper function to get arguments for color manipulation functions. * takes a list that contains a color like thing and a percentage */ - public function colorArgs($args) - { + public function colorArgs($args) { if ($args[0] != 'list' || count($args[2]) < 2) { return array(array('color', 0, 0, 0), 0); } @@ -1176,8 +1118,7 @@ class Lessc return array($color, $delta); } - protected function lib_darken($args) - { + protected function lib_darken($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); @@ -1185,8 +1126,7 @@ class Lessc return $this->toRGB($hsl); } - protected function lib_lighten($args) - { + protected function lib_lighten($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); @@ -1194,8 +1134,7 @@ class Lessc return $this->toRGB($hsl); } - protected function lib_saturate($args) - { + protected function lib_saturate($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); @@ -1203,8 +1142,7 @@ class Lessc return $this->toRGB($hsl); } - protected function lib_desaturate($args) - { + protected function lib_desaturate($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); @@ -1212,8 +1150,7 @@ class Lessc return $this->toRGB($hsl); } - protected function lib_spin($args) - { + protected function lib_spin($args) { list($color, $delta) = $this->colorArgs($args); $hsl = $this->toHSL($color); @@ -1226,57 +1163,49 @@ class Lessc return $this->toRGB($hsl); } - protected function lib_fadeout($args) - { + protected function lib_fadeout($args) { list($color, $delta) = $this->colorArgs($args); $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) - $delta / 100); return $color; } - protected function lib_fadein($args) - { + protected function lib_fadein($args) { list($color, $delta) = $this->colorArgs($args); $color[4] = $this->clamp((isset($color[4]) ? $color[4] : 1) + $delta / 100); return $color; } - protected function lib_hue($color) - { + protected function lib_hue($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[1]); } - protected function lib_saturation($color) - { + protected function lib_saturation($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[2]); } - protected function lib_lightness($color) - { + protected function lib_lightness($color) { $hsl = $this->toHSL($this->assertColor($color)); return round($hsl[3]); } // get the alpha of a color // defaults to 1 for non-colors or colors without an alpha - protected function lib_alpha($value) - { + protected function lib_alpha($value) { if (!is_null($color = $this->coerceColor($value))) { return isset($color[4]) ? $color[4] : 1; } } // set the alpha of the color - protected function lib_fade($args) - { + protected function lib_fade($args) { list($color, $alpha) = $this->colorArgs($args); $color[4] = $this->clamp($alpha / 100.0); return $color; } - protected function lib_percentage($arg) - { + protected function lib_percentage($arg) { $num = $this->assertNumber($arg); return array("number", $num * 100, "%"); } @@ -1292,8 +1221,7 @@ class Lessc * * @return array Color */ - protected function lib_tint($args) - { + protected function lib_tint($args) { $white = ['color', 255, 255, 255]; if ($args[0] == 'color') { return $this->lib_mix(['list', ',', [$white, $args]]); @@ -1315,8 +1243,7 @@ class Lessc * * @return array Color */ - protected function lib_shade($args) - { + protected function lib_shade($args) { $black = ['color', 0, 0, 0]; if ($args[0] == 'color') { return $this->lib_mix(['list', ',', [$black, $args]]); @@ -1330,8 +1257,7 @@ class Lessc // mixes two colors by weight // mix(@color1, @color2, [@weight: 50%]); // http://sass-lang.com/docs/yardoc/Sass/Script/Functions.html#mix-instance_method - protected function lib_mix($args) - { + protected function lib_mix($args) { if ($args[0] != "list" || count($args[2]) < 2) $this->throwError("mix expects (color1, color2, weight)"); @@ -1342,11 +1268,11 @@ class Lessc $first_a = $this->lib_alpha($first); $second_a = $this->lib_alpha($second); - if (isset($args[2][2])) { - $weight = $args[2][2][1] / 100.0; - } else { - $weight = 0.5; - } + if (isset($args[2][2])) { + $weight = $args[2][2][1] / 100.0; + } else { + $weight = 0.5; + } $w = $weight * 2 - 1; $a = $first_a - $second_a; @@ -1360,15 +1286,14 @@ class Lessc $w1 * $first[3] + $w2 * $second[3], ); - if ($first_a != 1.0 || $second_a != 1.0) { - $new[] = $first_a * $weight + $second_a * ($weight - 1); - } + if ($first_a != 1.0 || $second_a != 1.0) { + $new[] = $first_a * $weight + $second_a * ($weight - 1); + } return $this->fixColor($new); } - protected function lib_contrast($args) - { + protected function lib_contrast($args) { $darkColor = array('color', 0, 0, 0); $lightColor = array('color', 255, 255, 255); $threshold = 0.43; @@ -1378,7 +1303,8 @@ class Lessc $darkColor = (isset($args[2][1])) ? $this->assertColor($args[2][1]) : $darkColor; $lightColor = (isset($args[2][2])) ? $this->assertColor($args[2][2]) : $lightColor; $threshold = (isset($args[2][3])) ? $this->assertNumber($args[2][3]) : $threshold; - } else { + } + else { $inputColor = $this->assertColor($args); } @@ -1400,8 +1326,7 @@ class Lessc return $darkColor; } - private function toLuma($color) - { + private function toLuma($color) { list(, $r, $g, $b) = $this->coerceColor($color); $r = $r / 255; @@ -1415,27 +1340,23 @@ class Lessc return (0.2126 * $r) + (0.7152 * $g) + (0.0722 * $b); } - protected function lib_luma($color) - { + protected function lib_luma($color) { return array("number", round($this->toLuma($color) * 100, 8), "%"); } - public function assertColor($value, $error = "expected color value") - { + public function assertColor($value, $error = "expected color value") { $color = $this->coerceColor($value); if (is_null($color)) $this->throwError($error); return $color; } - public function assertNumber($value, $error = "expecting number") - { + public function assertNumber($value, $error = "expecting number") { if ($value[0] == "number") return $value[1]; $this->throwError($error); } - public function assertArgs($value, $expectedArgs, $name = "") - { + public function assertArgs($value, $expectedArgs, $name = "") { if ($expectedArgs == 1) { return $value; } else { @@ -1454,8 +1375,7 @@ class Lessc } } - protected function toHSL($color) - { + protected function toHSL($color) { if ($color[0] === 'hsl') { return $color; } @@ -1483,6 +1403,7 @@ class Lessc } elseif ($b == $max) { $H = 4.0 + ($r - $g) / ($max - $min); } + } $out = array('hsl', @@ -1498,8 +1419,7 @@ class Lessc return $out; } - protected function toRGB_helper($comp, $temp1, $temp2) - { + protected function toRGB_helper($comp, $temp1, $temp2) { if ($comp < 0) { $comp += 1.0; } elseif ($comp > 1) { @@ -1523,8 +1443,7 @@ class Lessc * Converts a hsl array into a color value in rgb. * Expects H to be in range of 0 to 360, S and L in 0 to 100 */ - protected function toRGB($color) - { + protected function toRGB($color) { if ($color[0] === 'color') { return $color; } @@ -1555,8 +1474,7 @@ class Lessc return $out; } - protected function clamp($v, $max = 1, $min = 0) - { + protected function clamp($v, $max = 1, $min = 0) { return min($max, max($min, $v)); } @@ -1564,8 +1482,7 @@ class Lessc * Convert the rgb, rgba, hsl color literals of function type * as returned by the parser into values of color type. */ - protected function funcToColor($func) - { + protected function funcToColor($func) { $fname = $func[1]; if ($func[2][0] != 'list') { // need a list of arguments @@ -1596,6 +1513,7 @@ class Lessc $hsl[] = 0; } return $this->toRGB($hsl); + } elseif ($fname == 'rgb' || $fname == 'rgba') { $components = array(); $i = 1; @@ -1627,8 +1545,7 @@ class Lessc return false; } - protected function reduce($value, $forExpression = false) - { + protected function reduce($value, $forExpression = false) { switch ($value[0]) { case "interpolate": $reduced = $this->reduce($value[1]); @@ -1694,18 +1611,18 @@ class Lessc $ret = call_user_func($f, $this->reduce($args, true), $this); - if (is_null($ret)) { - return array("string", "", array( - $name, "(", $args, ")" - )); - } + if (is_null($ret)) { + return array("string", "", array( + $name, "(", $args, ")" + )); + } // convert to a typed value if the result is a php primitive - if (is_numeric($ret)) { - $ret = array('number', $ret, ""); - } elseif (!is_array($ret)) { - $ret = array('keyword', $ret); - } + if (is_numeric($ret)) { + $ret = array('number', $ret, ""); + } elseif (!is_array($ret)) { + $ret = array('keyword', $ret); + } return $ret; } @@ -1746,8 +1663,7 @@ class Lessc // coerce a value for use in color operation - protected function coerceColor($value) - { + protected function coerceColor($value) { switch ($value[0]) { case 'color': return $value; case 'raw_color': @@ -1779,8 +1695,7 @@ class Lessc } // make something string like into a string - protected function coerceString($value) - { + protected function coerceString($value) { switch ($value[0]) { case "string": return $value; @@ -1791,22 +1706,19 @@ class Lessc } // turn list of length 1 into value type - protected function flattenList($value) - { + protected function flattenList($value) { if ($value[0] == "list" && count($value[2]) == 1) { return $this->flattenList($value[2][0]); } return $value; } - public function toBool($a) - { + public function toBool($a) { return $a ? self::$TRUE : self::$FALSE; } // evaluate an expression - protected function evaluate($exp) - { + protected function evaluate($exp) { list(, $op, $left, $right, $whiteBefore, $whiteAfter) = $exp; $left = $this->reduce($left, true); @@ -1855,8 +1767,7 @@ class Lessc return array("string", "", array($left, $paddedOp, $right)); } - protected function stringConcatenate($left, $right) - { + protected function stringConcatenate($left, $right) { if ($strLeft = $this->coerceString($left)) { if ($right[0] == "string") { $right[1] = ""; @@ -1873,8 +1784,7 @@ class Lessc // make sure a color's components don't go out of bounds - protected function fixColor($c) - { + protected function fixColor($c) { foreach (range(1, 3) as $i) { if ($c[$i] < 0) $c[$i] = 0; if ($c[$i] > 255) $c[$i] = 255; @@ -1883,23 +1793,20 @@ class Lessc return $c; } - protected function op_number_color($op, $lft, $rgt) - { + protected function op_number_color($op, $lft, $rgt) { if ($op == '+' || $op == '*') { return $this->op_color_number($op, $rgt, $lft); } } - protected function op_color_number($op, $lft, $rgt) - { + protected function op_color_number($op, $lft, $rgt) { if ($rgt[0] == '%') $rgt[1] /= 100; return $this->op_color_color($op, $lft, array_fill(1, count($lft) - 1, $rgt[1])); } - protected function op_color_color($op, $left, $right) - { + protected function op_color_color($op, $left, $right) { $out = array('color'); $max = count($left) > count($right) ? count($left) : count($right); foreach (range(1, $max - 1) as $i) { @@ -1931,8 +1838,7 @@ class Lessc return $this->fixColor($out); } - public function lib_red($color) - { + public function lib_red($color) { $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for red()'); @@ -1941,8 +1847,7 @@ class Lessc return $color[1]; } - public function lib_green($color) - { + public function lib_green($color) { $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for green()'); @@ -1951,8 +1856,7 @@ class Lessc return $color[2]; } - public function lib_blue($color) - { + public function lib_blue($color) { $color = $this->coerceColor($color); if (is_null($color)) { $this->throwError('color expected for blue()'); @@ -1963,8 +1867,7 @@ class Lessc // operator on two numbers - protected function op_number_number($op, $left, $right) - { + protected function op_number_number($op, $left, $right) { $unit = empty($left[2]) ? $right[2] : $left[2]; $value = 0; @@ -2003,8 +1906,7 @@ class Lessc /* environment functions */ - protected function makeOutputBlock($type, $selectors = null) - { + protected function makeOutputBlock($type, $selectors = null) { $b = new stdclass; $b->lines = array(); $b->children = array(); @@ -2015,8 +1917,7 @@ class Lessc } // the state of execution - protected function pushEnv($block = null) - { + protected function pushEnv($block = null) { $e = new stdclass; $e->parent = $this->env; $e->store = array(); @@ -2027,23 +1928,20 @@ class Lessc } // pop something off the stack - protected function popEnv() - { + protected function popEnv() { $old = $this->env; $this->env = $this->env->parent; return $old; } // set something in the current env - protected function set($name, $value) - { + protected function set($name, $value) { $this->env->store[$name] = $value; } // get the highest occurrence entry for a name - protected function get($name) - { + protected function get($name) { $current = $this->env; $isArguments = $name == $this->vPrefix.'arguments'; @@ -2064,12 +1962,11 @@ class Lessc } // inject array of unparsed strings into environment as variables - protected function injectVariables($args) - { + protected function injectVariables($args) { $this->pushEnv(); $parser = new lessc_parser($this, __METHOD__); foreach ($args as $name => $strValue) { - if ($name{0} !== '@') { + if ($name[0] !== '@') { $name = '@'.$name; } $parser->count = 0; @@ -2086,16 +1983,14 @@ class Lessc * Initialize any static state, can initialize parser for a file * $opts isn't used yet */ - public function __construct($fname = null) - { + public function __construct($fname = null) { if ($fname !== null) { // used for deprecated parse method $this->_parseFile = $fname; } } - public function compile($string, $name = null) - { + public function compile($string, $name = null) { $locale = setlocale(LC_NUMERIC, 0); setlocale(LC_NUMERIC, "C"); @@ -2121,8 +2016,7 @@ class Lessc return $out; } - public function compileFile($fname, $outFname = null) - { + public function compileFile($fname, $outFname = null) { if (!is_readable($fname)) { throw new Exception('load error: failed to find '.$fname); } @@ -2148,8 +2042,7 @@ class Lessc } // compile only if changed input has changed or output doesn't exist - public function checkedCompile($in, $out) - { + public function checkedCompile($in, $out) { if (!is_file($out) || filemtime($in) > filemtime($out)) { $this->compileFile($in, $out); return true; @@ -2177,8 +2070,7 @@ class Lessc * @param bool $force Force rebuild? * @return array lessphp cache structure */ - public function cachedCompile($in, $force = false) - { + public function cachedCompile($in, $force = false) { // assume no root $root = null; @@ -2219,12 +2111,12 @@ class Lessc // we were given initially. return $in; } + } // parse and compile buffer // This is deprecated - public function parse($str = null, $initialVariables = null) - { + public function parse($str = null, $initialVariables = null) { if (is_array($str)) { $initialVariables = $str; $str = null; @@ -2249,21 +2141,18 @@ class Lessc return $out; } - protected function makeParser($name) - { + protected function makeParser($name) { $parser = new lessc_parser($this, $name); $parser->writeComments = $this->preserveComments; return $parser; } - public function setFormatter($name) - { + public function setFormatter($name) { $this->formatterName = $name; } - protected function newFormatter() - { + protected function newFormatter() { $className = "lessc_formatter_lessjs"; if (!empty($this->formatterName)) { if (!is_string($this->formatterName)) @@ -2274,57 +2163,47 @@ class Lessc return new $className; } - public function setPreserveComments($preserve) - { + public function setPreserveComments($preserve) { $this->preserveComments = $preserve; } - public function registerFunction($name, $func) - { + public function registerFunction($name, $func) { $this->libFunctions[$name] = $func; } - public function unregisterFunction($name) - { + public function unregisterFunction($name) { unset($this->libFunctions[$name]); } - public function setVariables($variables) - { + public function setVariables($variables) { $this->registeredVars = array_merge($this->registeredVars, $variables); } - public function unsetVariable($name) - { + public function unsetVariable($name) { unset($this->registeredVars[$name]); } - public function setImportDir($dirs) - { + public function setImportDir($dirs) { $this->importDir = (array) $dirs; } - public function addImportDir($dir) - { + public function addImportDir($dir) { $this->importDir = (array) $this->importDir; $this->importDir[] = $dir; } - public function allParsedFiles() - { + public function allParsedFiles() { return $this->allParsedFiles; } - public function addParsedFile($file) - { + public function addParsedFile($file) { $this->allParsedFiles[realpath($file)] = filemtime($file); } /** * Uses the current value of $this->count to show line and line number */ - public function throwError($msg = null) - { + public function throwError($msg = null) { if ($this->sourceLoc >= 0) { $this->sourceParser->throwError($msg, $this->sourceLoc); } @@ -2333,16 +2212,14 @@ class Lessc // compile file $in to file $out if $in is newer than $out // returns true when it compiles, false otherwise - public static function ccompile($in, $out, $less = null) - { + public static function ccompile($in, $out, $less = null) { if ($less === null) { $less = new self; } return $less->checkedCompile($in, $out); } - public static function cexecute($in, $force = false, $less = null) - { + public static function cexecute($in, $force = false, $less = null) { if ($less === null) { $less = new self; } @@ -2503,8 +2380,7 @@ class Lessc // responsible for taking a string of LESS code and converting it into a // syntax tree -class lessc_parser -{ +class lessc_parser { protected static $nextBlockId = 0; // used to uniquely identify blocks protected static $precedence = array( @@ -2552,8 +2428,7 @@ class lessc_parser // caches preg escaped literals protected static $literalCache = array(); - public function __construct($lessc, $sourceName = null) - { + public function __construct($lessc, $sourceName = null) { $this->eatWhiteDefault = true; // reference to less needed for vPrefix, mPrefix, and parentSelector $this->lessc = $lessc; @@ -2583,8 +2458,7 @@ class lessc_parser * @throws exception * @return NULL|stdclass */ - public function parse($buffer) - { + public function parse($buffer) { $this->count = 0; $this->line = 1; @@ -2604,19 +2478,19 @@ class lessc_parser // parse the entire file while (false !== $this->parseChunk()); - if ($this->count != strlen($this->buffer)) + if ($this->count != strlen($this->buffer)) { - $this->throwError('parse error count '.$this->count.' != len buffer '.strlen($this->buffer)); - } + $this->throwError('parse error count '.$this->count.' != len buffer '.strlen($this->buffer)); + } // TODO report where the block was opened - if (!property_exists($this->env, 'parent') || !is_null($this->env->parent)) + if (!property_exists($this->env, 'parent') || !is_null($this->env->parent)) { - throw new exception('parse error: unclosed block'); - } + throw new exception('parse error: unclosed block'); + } return $this->env; - } + } /** * Parse a single chunk off the head of the buffer and append it to the @@ -2654,1300 +2528,1257 @@ class lessc_parser * position into $s. Then if a chain fails, use $this->seek($s) to * go back where we started. */ - protected function parseChunk() - { - if (empty($this->buffer)) return false; - $s = $this->seek(); + protected function parseChunk() { + if (empty($this->buffer)) return false; + $s = $this->seek(); - if ($this->whitespace()) { - return true; - } - - // setting a property - if ($this->keyword($key) && $this->assign() && - $this->propertyValue($value, $key) && $this->end() - ) { - $this->append(array('assign', $key, $value), $s); + if ($this->whitespace()) { return true; - } else { - $this->seek($s); - } + } - - // look for special css blocks - if ($this->literal('@', false)) { - $this->count--; - - // media - if ($this->literal('@media')) { - if (($this->mediaQueryList($mediaQueries) || true) - && $this->literal('{') - ) { - $media = $this->pushSpecialBlock("media"); - $media->queries = is_null($mediaQueries) ? array() : $mediaQueries; - return true; + // setting a property + if ($this->keyword($key) && $this->assign() && + $this->propertyValue($value, $key) && $this->end() + ) { + $this->append(array('assign', $key, $value), $s); + return true; } else { $this->seek($s); - return false; } - } - if ($this->literal("@", false) && $this->keyword($dirName)) { - if ($this->isDirective($dirName, $this->blockDirectives)) { - if (($this->openString("{", $dirValue, null, array(";")) || true) && - $this->literal("{") - ) { - $dir = $this->pushSpecialBlock("directive"); - $dir->name = $dirName; - if (isset($dirValue)) $dir->value = $dirValue; - return true; + + // look for special css blocks + if ($this->literal('@', false)) { + $this->count--; + + // media + if ($this->literal('@media')) { + if (($this->mediaQueryList($mediaQueries) || true) + && $this->literal('{') + ) { + $media = $this->pushSpecialBlock("media"); + $media->queries = is_null($mediaQueries) ? array() : $mediaQueries; + return true; + } else { + $this->seek($s); + return false; + } } - } elseif ($this->isDirective($dirName, $this->lineDirectives)) { - if ($this->propertyValue($dirValue) && $this->end()) { - $this->append(array("directive", $dirName, $dirValue)); - return true; - } - } - } - $this->seek($s); - } - - // setting a variable - if ($this->variable($var) && $this->assign() && - $this->propertyValue($value) && $this->end() - ) { - $this->append(array('assign', $var, $value), $s); - return true; - } else { - $this->seek($s); - } - - if ($this->import($importValue)) { - $this->append($importValue, $s); - return true; - } - - // opening parametric mixin - if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) && - ($this->guards($guards) || true) && - $this->literal('{') - ) { - $block = $this->pushBlock($this->fixTags(array($tag))); - $block->args = $args; - $block->isVararg = $isVararg; - if (!empty($guards)) $block->guards = $guards; - return true; - } else { - $this->seek($s); - } - - // opening a simple block - if ($this->tags($tags) && $this->literal('{', false)) { - $tags = $this->fixTags($tags); - $this->pushBlock($tags); - return true; - } else { - $this->seek($s); - } - - // closing a block - if ($this->literal('}', false)) { - try { - $block = $this->pop(); - } catch (exception $e) { - $this->seek($s); - $this->throwError($e->getMessage()); - } - - $hidden = false; - if (is_null($block->type)) { - $hidden = true; - if (!isset($block->args)) { - foreach ($block->tags as $tag) { - if (!is_string($tag) || $tag{0} != $this->lessc->mPrefix) { - $hidden = false; - break; + if ($this->literal("@", false) && $this->keyword($dirName)) { + if ($this->isDirective($dirName, $this->blockDirectives)) { + if (($this->openString("{", $dirValue, null, array(";")) || true) && + $this->literal("{") + ) { + $dir = $this->pushSpecialBlock("directive"); + $dir->name = $dirName; + if (isset($dirValue)) $dir->value = $dirValue; + return true; + } + } elseif ($this->isDirective($dirName, $this->lineDirectives)) { + if ($this->propertyValue($dirValue) && $this->end()) { + $this->append(array("directive", $dirName, $dirValue)); + return true; + } } } + + $this->seek($s); } - foreach ($block->tags as $tag) { - if (is_string($tag)) { - $this->env->children[$tag][] = $block; + // setting a variable + if ($this->variable($var) && $this->assign() && + $this->propertyValue($value) && $this->end() + ) { + $this->append(array('assign', $var, $value), $s); + return true; + } else { + $this->seek($s); } - } - } - if (!$hidden) { - $this->append(array('block', $block), $s); - } + if ($this->import($importValue)) { + $this->append($importValue, $s); + return true; + } - // this is done here so comments aren't bundled into he block that - // was just closed - $this->whitespace(); - return true; - } - - // mixin - if ($this->mixinTags($tags) && - ($this->argumentDef($argv, $isVararg) || true) && - ($this->keyword($suffix) || true) && $this->end() + // opening parametric mixin + if ($this->tag($tag, true) && $this->argumentDef($args, $isVararg) && + ($this->guards($guards) || true) && + $this->literal('{') ) { - $tags = $this->fixTags($tags); - $this->append(array('mixin', $tags, $argv, $suffix), $s); - return true; - } else { - $this->seek($s); + $block = $this->pushBlock($this->fixTags(array($tag))); + $block->args = $args; + $block->isVararg = $isVararg; + if (!empty($guards)) $block->guards = $guards; + return true; + } else { + $this->seek($s); + } + + // opening a simple block + if ($this->tags($tags) && $this->literal('{', false)) { + $tags = $this->fixTags($tags); + $this->pushBlock($tags); + return true; + } else { + $this->seek($s); + } + + // closing a block + if ($this->literal('}', false)) { + try { + $block = $this->pop(); + } catch (exception $e) { + $this->seek($s); + $this->throwError($e->getMessage()); + } + + $hidden = false; + if (is_null($block->type)) { + $hidden = true; + if (!isset($block->args)) { + foreach ($block->tags as $tag) { + if (!is_string($tag) || $tag[0] != $this->lessc->mPrefix) { + $hidden = false; + break; + } + } + } + + foreach ($block->tags as $tag) { + if (is_string($tag)) { + $this->env->children[$tag][] = $block; + } + } + } + + if (!$hidden) { + $this->append(array('block', $block), $s); + } + + // this is done here so comments aren't bundled into he block that + // was just closed + $this->whitespace(); + return true; + } + + // mixin + if ($this->mixinTags($tags) && + ($this->argumentDef($argv, $isVararg) || true) && + ($this->keyword($suffix) || true) && $this->end() + ) { + $tags = $this->fixTags($tags); + $this->append(array('mixin', $tags, $argv, $suffix), $s); + return true; + } else { + $this->seek($s); + } + + // spare ; + if ($this->literal(';')) return true; + + return false; // got nothing, throw error } - // spare ; - if ($this->literal(';')) return true; + protected function isDirective($dirname, $directives) { + // TODO: cache pattern in parser + $pattern = implode("|", + array_map(array("lessc", "preg_quote"), $directives)); + $pattern = '/^(-[a-z-]+-)?('.$pattern.')$/i'; - return false; // got nothing, throw error - } - - protected function isDirective($dirname, $directives) - { - // TODO: cache pattern in parser - $pattern = implode("|", - array_map(array("lessc", "preg_quote"), $directives)); - $pattern = '/^(-[a-z-]+-)?('.$pattern.')$/i'; - - return preg_match($pattern, $dirname); - } - - protected function fixTags($tags) - { - // move @ tags out of variable namespace - foreach ($tags as &$tag) { - if ($tag{0} == $this->lessc->vPrefix) - $tag[0] = $this->lessc->mPrefix; + return preg_match($pattern, $dirname); + } + + protected function fixTags($tags) { + // move @ tags out of variable namespace + foreach ($tags as &$tag) { + if ($tag[0] == $this->lessc->vPrefix) + $tag[0] = $this->lessc->mPrefix; + } + return $tags; } - return $tags; - } // a list of expressions - protected function expressionList(&$exps) - { - $values = array(); + protected function expressionList(&$exps) { + $values = array(); - while ($this->expression($exp)) { - $values[] = $exp; + while ($this->expression($exp)) { + $values[] = $exp; + } + + if (count($values) == 0) return false; + + $exps = lessc::compressList($values, ' '); + return true; } - if (count($values) == 0) return false; - - $exps = lessc::compressList($values, ' '); - return true; - } - /** * Attempt to consume an expression. * @link http://en.wikipedia.org/wiki/Operator-precedence_parser#Pseudo-code */ - protected function expression(&$out) - { - if ($this->value($lhs)) { - $out = $this->expHelper($lhs, 0); + protected function expression(&$out) { + if ($this->value($lhs)) { + $out = $this->expHelper($lhs, 0); - // look for / shorthand - if (!empty($this->env->supressedDivision)) { - unset($this->env->supressedDivision); - $s = $this->seek(); - if ($this->literal("/") && $this->value($rhs)) { - $out = array("list", "", - array($out, array("keyword", "/"), $rhs)); - } else { - $this->seek($s); + // look for / shorthand + if (!empty($this->env->supressedDivision)) { + unset($this->env->supressedDivision); + $s = $this->seek(); + if ($this->literal("/") && $this->value($rhs)) { + $out = array("list", "", + array($out, array("keyword", "/"), $rhs)); + } else { + $this->seek($s); + } } - } - return true; + return true; + } + return false; } - return false; - } /** * recursively parse infix equation with $lhs at precedence $minP */ - protected function expHelper($lhs, $minP) - { - $this->inExp = true; - $ss = $this->seek(); + protected function expHelper($lhs, $minP) { + $this->inExp = true; + $ss = $this->seek(); - while (true) { - $whiteBefore = isset($this->buffer[$this->count - 1]) && - ctype_space($this->buffer[$this->count - 1]); - - // If there is whitespace before the operator, then we require - // whitespace after the operator for it to be an expression - $needWhite = $whiteBefore && !$this->inParens; - - if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) { - if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) { - foreach (self::$supressDivisionProps as $pattern) { - if (preg_match($pattern, $this->env->currentProperty)) { - $this->env->supressedDivision = true; - break 2; - } - } - } - - - $whiteAfter = isset($this->buffer[$this->count - 1]) && + while (true) { + $whiteBefore = isset($this->buffer[$this->count - 1]) && ctype_space($this->buffer[$this->count - 1]); - if (!$this->value($rhs)) break; + // If there is whitespace before the operator, then we require + // whitespace after the operator for it to be an expression + $needWhite = $whiteBefore && !$this->inParens; - // peek for next operator to see what to do with rhs - if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) { - $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + if ($this->match(self::$operatorString.($needWhite ? '\s' : ''), $m) && self::$precedence[$m[1]] >= $minP) { + if (!$this->inParens && isset($this->env->currentProperty) && $m[1] == "/" && empty($this->env->supressedDivision)) { + foreach (self::$supressDivisionProps as $pattern) { + if (preg_match($pattern, $this->env->currentProperty)) { + $this->env->supressedDivision = true; + break 2; + } + } + } + + + $whiteAfter = isset($this->buffer[$this->count - 1]) && + ctype_space($this->buffer[$this->count - 1]); + + if (!$this->value($rhs)) break; + + // peek for next operator to see what to do with rhs + if ($this->peek(self::$operatorString, $next) && self::$precedence[$next[1]] > self::$precedence[$m[1]]) { + $rhs = $this->expHelper($rhs, self::$precedence[$next[1]]); + } + + $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter); + $ss = $this->seek(); + + continue; } - $lhs = array('expression', $m[1], $lhs, $rhs, $whiteBefore, $whiteAfter); - $ss = $this->seek(); - - continue; - } - - break; - } - - $this->seek($ss); - - return $lhs; - } - - // consume a list of values for a property - public function propertyValue(&$value, $keyName = null) - { - $values = array(); - - if ($keyName !== null) $this->env->currentProperty = $keyName; - - $s = null; - while ($this->expressionList($v)) { - $values[] = $v; - $s = $this->seek(); - if (!$this->literal(',')) break; - } - - if ($s) $this->seek($s); - - if ($keyName !== null) unset($this->env->currentProperty); - - if (count($values) == 0) return false; - - $value = lessc::compressList($values, ', '); - return true; - } - - protected function parenValue(&$out) - { - $s = $this->seek(); - - // speed shortcut - if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") { - return false; - } - - $inParens = $this->inParens; - if ($this->literal("(") && - ($this->inParens = true) && $this->expression($exp) && - $this->literal(")") - ) { - $out = $exp; - $this->inParens = $inParens; - return true; - } else { - $this->inParens = $inParens; - $this->seek($s); - } - - return false; - } - - // a single value - protected function value(&$value) - { - $s = $this->seek(); - - // speed shortcut - if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") { - // negation - if ($this->literal("-", false) && - (($this->variable($inner) && $inner = array("variable", $inner)) || - $this->unit($inner) || - $this->parenValue($inner)) - ) { - $value = array("unary", "-", $inner); - return true; - } else { - $this->seek($s); - } - } - - if ($this->parenValue($value)) return true; - if ($this->unit($value)) return true; - if ($this->color($value)) return true; - if ($this->func($value)) return true; - if ($this->string($value)) return true; - - if ($this->keyword($word)) { - $value = array('keyword', $word); - return true; - } - - // try a variable - if ($this->variable($var)) { - $value = array('variable', $var); - return true; - } - - // unquote string (should this work on any type? - if ($this->literal("~") && $this->string($str)) { - $value = array("escape", $str); - return true; - } else { - $this->seek($s); - } - - // css hack: \0 - if ($this->literal('\\') && $this->match('([0-9]+)', $m)) { - $value = array('keyword', '\\'.$m[1]); - return true; - } else { - $this->seek($s); - } - - return false; - } - - // an import statement - protected function import(&$out) - { - if (!$this->literal('@import')) return false; - - // @import "something.css" media; - // @import url("something.css") media; - // @import url(something.css) media; - - if ($this->propertyValue($value)) { - $out = array("import", $value); - return true; - } - } - - protected function mediaQueryList(&$out) - { - if ($this->genericList($list, "mediaQuery", ",", false)) { - $out = $list[2]; - return true; - } - return false; - } - - protected function mediaQuery(&$out) - { - $s = $this->seek(); - - $expressions = null; - $parts = array(); - - if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) { - $prop = array("mediaType"); - if (isset($only)) $prop[] = "only"; - if (isset($not)) $prop[] = "not"; - $prop[] = $mediaType; - $parts[] = $prop; - } else { - $this->seek($s); - } - - - if (!empty($mediaType) && !$this->literal("and")) { - // ~ - } else { - $this->genericList($expressions, "mediaExpression", "and", false); - if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]); - } - - if (count($parts) == 0) { - $this->seek($s); - return false; - } - - $out = $parts; - return true; - } - - protected function mediaExpression(&$out) - { - $s = $this->seek(); - $value = null; - if ($this->literal("(") && - $this->keyword($feature) && - ($this->literal(":") && $this->expression($value) || true) && - $this->literal(")") - ) { - $out = array("mediaExp", $feature); - if ($value) $out[] = $value; - return true; - } elseif ($this->variable($variable)) { - $out = array('variable', $variable); - return true; - } - - $this->seek($s); - return false; - } - - // an unbounded string stopped by $end - protected function openString($end, &$out, $nestingOpen = null, $rejectStrs = null) - { - $oldWhite = $this->eatWhiteDefault; - $this->eatWhiteDefault = false; - - $stop = array("'", '"', "@{", $end); - $stop = array_map(array("lessc", "preg_quote"), $stop); - // $stop[] = self::$commentMulti; - - if (!is_null($rejectStrs)) { - $stop = array_merge($stop, $rejectStrs); - } - - $patt = '(.*?)('.implode("|", $stop).')'; - - $nestingLevel = 0; - - $content = array(); - while ($this->match($patt, $m, false)) { - if (!empty($m[1])) { - $content[] = $m[1]; - if ($nestingOpen) { - $nestingLevel += substr_count($m[1], $nestingOpen); - } - } - - $tok = $m[2]; - - $this->count -= strlen($tok); - if ($tok == $end) { - if ($nestingLevel == 0) { - break; - } else { - $nestingLevel--; - } - } - - if (($tok == "'" || $tok == '"') && $this->string($str)) { - $content[] = $str; - continue; - } - - if ($tok == "@{" && $this->interpolation($inter)) { - $content[] = $inter; - continue; - } - - if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) { break; } - $content[] = $tok; - $this->count += strlen($tok); + $this->seek($ss); + + return $lhs; } - $this->eatWhiteDefault = $oldWhite; + // consume a list of values for a property + public function propertyValue(&$value, $keyName = null) { + $values = array(); - if (count($content) == 0) return false; + if ($keyName !== null) $this->env->currentProperty = $keyName; - // trim the end - if (is_string(end($content))) { - $content[count($content) - 1] = rtrim(end($content)); + $s = null; + while ($this->expressionList($v)) { + $values[] = $v; + $s = $this->seek(); + if (!$this->literal(',')) break; + } + + if ($s) $this->seek($s); + + if ($keyName !== null) unset($this->env->currentProperty); + + if (count($values) == 0) return false; + + $value = lessc::compressList($values, ', '); + return true; } - $out = array("string", "", $content); - return true; - } + protected function parenValue(&$out) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "(") { + return false; + } + + $inParens = $this->inParens; + if ($this->literal("(") && + ($this->inParens = true) && $this->expression($exp) && + $this->literal(")") + ) { + $out = $exp; + $this->inParens = $inParens; + return true; + } else { + $this->inParens = $inParens; + $this->seek($s); + } + + return false; + } + + // a single value + protected function value(&$value) { + $s = $this->seek(); + + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "-") { + // negation + if ($this->literal("-", false) && + (($this->variable($inner) && $inner = array("variable", $inner)) || + $this->unit($inner) || + $this->parenValue($inner)) + ) { + $value = array("unary", "-", $inner); + return true; + } else { + $this->seek($s); + } + } + + if ($this->parenValue($value)) return true; + if ($this->unit($value)) return true; + if ($this->color($value)) return true; + if ($this->func($value)) return true; + if ($this->string($value)) return true; + + if ($this->keyword($word)) { + $value = array('keyword', $word); + return true; + } + + // try a variable + if ($this->variable($var)) { + $value = array('variable', $var); + return true; + } + + // unquote string (should this work on any type? + if ($this->literal("~") && $this->string($str)) { + $value = array("escape", $str); + return true; + } else { + $this->seek($s); + } + + // css hack: \0 + if ($this->literal('\\') && $this->match('([0-9]+)', $m)) { + $value = array('keyword', '\\'.$m[1]); + return true; + } else { + $this->seek($s); + } - protected function string(&$out) - { - $s = $this->seek(); - if ($this->literal('"', false)) { - $delim = '"'; - } elseif ($this->literal("'", false)) { - $delim = "'"; - } else { return false; } - $content = array(); + // an import statement + protected function import(&$out) { + if (!$this->literal('@import')) return false; - // look for either ending delim , escape, or string interpolation - $patt = '([^\n]*?)(@\{|\\\\|'. - lessc::preg_quote($delim).')'; + // @import "something.css" media; + // @import url("something.css") media; + // @import url(something.css) media; + if ($this->propertyValue($value)) { + $out = array("import", $value); + return true; + } + } + + protected function mediaQueryList(&$out) { + if ($this->genericList($list, "mediaQuery", ",", false)) { + $out = $list[2]; + return true; + } + return false; + } + + protected function mediaQuery(&$out) { + $s = $this->seek(); + + $expressions = null; + $parts = array(); + + if (($this->literal("only") && ($only = true) || $this->literal("not") && ($not = true) || true) && $this->keyword($mediaType)) { + $prop = array("mediaType"); + if (isset($only)) $prop[] = "only"; + if (isset($not)) $prop[] = "not"; + $prop[] = $mediaType; + $parts[] = $prop; + } else { + $this->seek($s); + } + + + if (!empty($mediaType) && !$this->literal("and")) { + // ~ + } else { + $this->genericList($expressions, "mediaExpression", "and", false); + if (is_array($expressions)) $parts = array_merge($parts, $expressions[2]); + } + + if (count($parts) == 0) { + $this->seek($s); + return false; + } + + $out = $parts; + return true; + } + + protected function mediaExpression(&$out) { + $s = $this->seek(); + $value = null; + if ($this->literal("(") && + $this->keyword($feature) && + ($this->literal(":") && $this->expression($value) || true) && + $this->literal(")") + ) { + $out = array("mediaExp", $feature); + if ($value) $out[] = $value; + return true; + } elseif ($this->variable($variable)) { + $out = array('variable', $variable); + return true; + } + + $this->seek($s); + return false; + } + + // an unbounded string stopped by $end + protected function openString($end, &$out, $nestingOpen = null, $rejectStrs = null) { $oldWhite = $this->eatWhiteDefault; $this->eatWhiteDefault = false; - while ($this->match($patt, $m, false)) { - $content[] = $m[1]; - if ($m[2] == "@{") { - $this->count -= strlen($m[2]); - if ($this->interpolation($inter)) { - $content[] = $inter; - } else { - $this->count += strlen($m[2]); - $content[] = "@{"; // ignore it - } - } elseif ($m[2] == '\\') { - $content[] = $m[2]; - if ($this->literal($delim, false)) { - $content[] = $delim; - } - } else { - $this->count -= strlen($delim); - break; // delim + $stop = array("'", '"', "@{", $end); + $stop = array_map(array("lessc", "preg_quote"), $stop); + // $stop[] = self::$commentMulti; + + if (!is_null($rejectStrs)) { + $stop = array_merge($stop, $rejectStrs); + } + + $patt = '(.*?)('.implode("|", $stop).')'; + + $nestingLevel = 0; + + $content = array(); + while ($this->match($patt, $m, false)) { + if (!empty($m[1])) { + $content[] = $m[1]; + if ($nestingOpen) { + $nestingLevel += substr_count($m[1], $nestingOpen); + } + } + + $tok = $m[2]; + + $this->count -= strlen($tok); + if ($tok == $end) { + if ($nestingLevel == 0) { + break; + } else { + $nestingLevel--; + } + } + + if (($tok == "'" || $tok == '"') && $this->string($str)) { + $content[] = $str; + continue; + } + + if ($tok == "@{" && $this->interpolation($inter)) { + $content[] = $inter; + continue; + } + + if (!empty($rejectStrs) && in_array($tok, $rejectStrs)) { + break; + } + + $content[] = $tok; + $this->count += strlen($tok); } - } $this->eatWhiteDefault = $oldWhite; - if ($this->literal($delim)) { - $out = array("string", $delim, $content); + if (count($content) == 0) return false; + + // trim the end + if (is_string(end($content))) { + $content[count($content) - 1] = rtrim(end($content)); + } + + $out = array("string", "", $content); return true; } - $this->seek($s); - return false; - } + protected function string(&$out) { + $s = $this->seek(); + if ($this->literal('"', false)) { + $delim = '"'; + } elseif ($this->literal("'", false)) { + $delim = "'"; + } else { + return false; + } - protected function interpolation(&$out) - { - $oldWhite = $this->eatWhiteDefault; - $this->eatWhiteDefault = true; + $content = array(); + + // look for either ending delim , escape, or string interpolation + $patt = '([^\n]*?)(@\{|\\\\|'. + lessc::preg_quote($delim).')'; + + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; + + while ($this->match($patt, $m, false)) { + $content[] = $m[1]; + if ($m[2] == "@{") { + $this->count -= strlen($m[2]); + if ($this->interpolation($inter)) { + $content[] = $inter; + } else { + $this->count += strlen($m[2]); + $content[] = "@{"; // ignore it + } + } elseif ($m[2] == '\\') { + $content[] = $m[2]; + if ($this->literal($delim, false)) { + $content[] = $delim; + } + } else { + $this->count -= strlen($delim); + break; // delim + } + } - $s = $this->seek(); - if ($this->literal("@{") && - $this->openString("}", $interp, null, array("'", '"', ";")) && - $this->literal("}", false) - ) { - $out = array("interpolate", $interp); $this->eatWhiteDefault = $oldWhite; - if ($this->eatWhiteDefault) $this->whitespace(); + + if ($this->literal($delim)) { + $out = array("string", $delim, $content); + return true; + } + + $this->seek($s); + return false; + } + + protected function interpolation(&$out) { + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = true; + + $s = $this->seek(); + if ($this->literal("@{") && + $this->openString("}", $interp, null, array("'", '"', ";")) && + $this->literal("}", false) + ) { + $out = array("interpolate", $interp); + $this->eatWhiteDefault = $oldWhite; + if ($this->eatWhiteDefault) $this->whitespace(); + return true; + } + + $this->eatWhiteDefault = $oldWhite; + $this->seek($s); + return false; + } + + protected function unit(&$unit) { + // speed shortcut + if (isset($this->buffer[$this->count])) { + $char = $this->buffer[$this->count]; + if (!ctype_digit($char) && $char != ".") return false; + } + + if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) { + $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]); return true; - } - - $this->eatWhiteDefault = $oldWhite; - $this->seek($s); + } return false; - } - - protected function unit(&$unit) - { - // speed shortcut - if (isset($this->buffer[$this->count])) { - $char = $this->buffer[$this->count]; - if (!ctype_digit($char) && $char != ".") return false; } - if ($this->match('([0-9]+(?:\.[0-9]*)?|\.[0-9]+)([%a-zA-Z]+)?', $m)) { - $unit = array("number", $m[1], empty($m[2]) ? "" : $m[2]); - return true; - } - return false; - } - // a # color - protected function color(&$out) - { - if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) { - if (strlen($m[1]) > 7) { - $out = array("string", "", array($m[1])); - } else { - $out = array("raw_color", $m[1]); + protected function color(&$out) { + if ($this->match('(#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3}))', $m)) { + if (strlen($m[1]) > 7) { + $out = array("string", "", array($m[1])); + } else { + $out = array("raw_color", $m[1]); + } + return true; } - return true; - } - return false; - } + return false; + } // consume an argument definition list surrounded by () // each argument is a variable name with optional value // or at the end a ... or a variable named followed by ... // arguments are separated by , unless a ; is in the list, then ; is the // delimiter. - protected function argumentDef(&$args, &$isVararg) - { - $s = $this->seek(); - if (!$this->literal('(')) { - return false; - } - - $values = array(); - $delim = ","; - $method = "expressionList"; - - $isVararg = false; - while (true) { - if ($this->literal("...")) { - $isVararg = true; - break; + protected function argumentDef(&$args, &$isVararg) { + $s = $this->seek(); + if (!$this->literal('(')) { + return false; } - if ($this->$method($value)) { - if ($value[0] == "variable") { - $arg = array("arg", $value[1]); - $ss = $this->seek(); + $values = array(); + $delim = ","; + $method = "expressionList"; - if ($this->assign() && $this->$method($rhs)) { - $arg[] = $rhs; - } else { - $this->seek($ss); - if ($this->literal("...")) { - $arg[0] = "rest"; - $isVararg = true; - } - } - - $values[] = $arg; - if ($isVararg) { - break; - } - continue; - } else { - $values[] = array("lit", $value); + $isVararg = false; + while (true) { + if ($this->literal("...")) { + $isVararg = true; + break; } - } + if ($this->$method($value)) { + if ($value[0] == "variable") { + $arg = array("arg", $value[1]); + $ss = $this->seek(); - if (!$this->literal($delim)) { - if ($delim == "," && $this->literal(";")) { - // found new delim, convert existing args - $delim = ";"; - $method = "propertyValue"; - - // transform arg list - if (isset($values[1])) { // 2 items - $newList = array(); - foreach ($values as $i => $arg) { - switch ($arg[0]) { - case "arg": - if ($i) { - $this->throwError("Cannot mix ; and , as delimiter types"); - } - $newList[] = $arg[2]; - break; - case "lit": - $newList[] = $arg[1]; - break; - case "rest": - $this->throwError("Unexpected rest before semicolon"); + if ($this->assign() && $this->$method($rhs)) { + $arg[] = $rhs; + } else { + $this->seek($ss); + if ($this->literal("...")) { + $arg[0] = "rest"; + $isVararg = true; } } - $newList = array("list", ", ", $newList); - - switch ($values[0][0]) { - case "arg": - $newArg = array("arg", $values[0][1], $newList); - break; - case "lit": - $newArg = array("lit", $newList); - break; + $values[] = $arg; + if ($isVararg) { + break; } - } elseif ($values) { // 1 item - $newArg = $values[0]; + continue; + } else { + $values[] = array("lit", $value); } + } - if ($newArg) { - $values = array($newArg); + + if (!$this->literal($delim)) { + if ($delim == "," && $this->literal(";")) { + // found new delim, convert existing args + $delim = ";"; + $method = "propertyValue"; + + // transform arg list + if (isset($values[1])) { // 2 items + $newList = array(); + foreach ($values as $i => $arg) { + switch ($arg[0]) { + case "arg": + if ($i) { + $this->throwError("Cannot mix ; and , as delimiter types"); + } + $newList[] = $arg[2]; + break; + case "lit": + $newList[] = $arg[1]; + break; + case "rest": + $this->throwError("Unexpected rest before semicolon"); + } + } + + $newList = array("list", ", ", $newList); + + switch ($values[0][0]) { + case "arg": + $newArg = array("arg", $values[0][1], $newList); + break; + case "lit": + $newArg = array("lit", $newList); + break; + } + + } elseif ($values) { // 1 item + $newArg = $values[0]; + } + + if ($newArg) { + $values = array($newArg); + } + } else { + break; } - } else { - break; } } + + if (!$this->literal(')')) { + $this->seek($s); + return false; + } + + $args = $values; + + return true; } - if (!$this->literal(')')) { + // consume a list of tags + // this accepts a hanging delimiter + protected function tags(&$tags, $simple = false, $delim = ',') { + $tags = array(); + while ($this->tag($tt, $simple)) { + $tags[] = $tt; + if (!$this->literal($delim)) break; + } + if (count($tags) == 0) return false; + + return true; + } + + // list of tags of specifying mixin path + // optionally separated by > (lazy, accepts extra >) + protected function mixinTags(&$tags) { + $tags = array(); + while ($this->tag($tt, true)) { + $tags[] = $tt; + $this->literal(">"); + } + + if (!$tags) { + return false; + } + + return true; + } + + // a bracketed value (contained within in a tag definition) + protected function tagBracket(&$parts, &$hasExpression) { + // speed shortcut + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { + return false; + } + + $s = $this->seek(); + + $hasInterpolation = false; + + if ($this->literal("[", false)) { + $attrParts = array("["); + // keyword, string, operator + while (true) { + if ($this->literal("]", false)) { + $this->count--; + break; // get out early + } + + if ($this->match('\s+', $m)) { + $attrParts[] = " "; + continue; + } + if ($this->string($str)) { + // escape parent selector, (yuck) + foreach ($str[2] as &$chunk) { + $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk); + } + + $attrParts[] = $str; + $hasInterpolation = true; + continue; + } + + if ($this->keyword($word)) { + $attrParts[] = $word; + continue; + } + + if ($this->interpolation($inter)) { + $attrParts[] = $inter; + $hasInterpolation = true; + continue; + } + + // operator, handles attr namespace too + if ($this->match('[|-~\$\*\^=]+', $m)) { + $attrParts[] = $m[0]; + continue; + } + + break; + } + + if ($this->literal("]", false)) { + $attrParts[] = "]"; + foreach ($attrParts as $part) { + $parts[] = $part; + } + $hasExpression = $hasExpression || $hasInterpolation; + return true; + } + $this->seek($s); + } + $this->seek($s); return false; } - $args = $values; + // a space separated list of selectors + protected function tag(&$tag, $simple = false) { + if ($simple) { + $chars = '^@,:;{}\][>\(\) "\''; + } else { + $chars = '^@,;{}["\''; + } + $s = $this->seek(); - return true; - } + $hasExpression = false; + $parts = array(); + while ($this->tagBracket($parts, $hasExpression)); - // consume a list of tags - // this accepts a hanging delimiter - protected function tags(&$tags, $simple = false, $delim = ',') - { - $tags = array(); - while ($this->tag($tt, $simple)) { - $tags[] = $tt; - if (!$this->literal($delim)) break; - } - if (count($tags) == 0) return false; + $oldWhite = $this->eatWhiteDefault; + $this->eatWhiteDefault = false; - return true; - } - - // list of tags of specifying mixin path - // optionally separated by > (lazy, accepts extra >) - protected function mixinTags(&$tags) - { - $tags = array(); - while ($this->tag($tt, true)) { - $tags[] = $tt; - $this->literal(">"); - } - - if (!$tags) { - return false; - } - - return true; - } - - // a bracketed value (contained within in a tag definition) - protected function tagBracket(&$parts, &$hasExpression) - { - // speed shortcut - if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] != "[") { - return false; - } - - $s = $this->seek(); - - $hasInterpolation = false; - - if ($this->literal("[", false)) { - $attrParts = array("["); - // keyword, string, operator while (true) { - if ($this->literal("]", false)) { - $this->count--; - break; // get out early - } + if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { + $parts[] = $m[1]; + if ($simple) break; - if ($this->match('\s+', $m)) { - $attrParts[] = " "; + while ($this->tagBracket($parts, $hasExpression)); continue; } - if ($this->string($str)) { - // escape parent selector, (yuck) - foreach ($str[2] as &$chunk) { - $chunk = str_replace($this->lessc->parentSelector, "$&$", $chunk); + + if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") { + if ($this->interpolation($interp)) { + $hasExpression = true; + $interp[2] = true; // don't unescape + $parts[] = $interp; + continue; } - $attrParts[] = $str; - $hasInterpolation = true; - continue; + if ($this->literal("@")) { + $parts[] = "@"; + continue; + } } - if ($this->keyword($word)) { - $attrParts[] = $word; - continue; - } - - if ($this->interpolation($inter)) { - $attrParts[] = $inter; - $hasInterpolation = true; - continue; - } - - // operator, handles attr namespace too - if ($this->match('[|-~\$\*\^=]+', $m)) { - $attrParts[] = $m[0]; + if ($this->unit($unit)) { // for keyframes + $parts[] = $unit[1]; + $parts[] = $unit[2]; continue; } break; } - if ($this->literal("]", false)) { - $attrParts[] = "]"; - foreach ($attrParts as $part) { - $parts[] = $part; - } - $hasExpression = $hasExpression || $hasInterpolation; - return true; - } - $this->seek($s); - } - - $this->seek($s); - return false; - } - - // a space separated list of selectors - protected function tag(&$tag, $simple = false) - { - if ($simple) { - $chars = '^@,:;{}\][>\(\) "\''; - } else { - $chars = '^@,;{}["\''; - } - $s = $this->seek(); - - $hasExpression = false; - $parts = array(); - while ($this->tagBracket($parts, $hasExpression)); - - $oldWhite = $this->eatWhiteDefault; - $this->eatWhiteDefault = false; - - while (true) { - if ($this->match('(['.$chars.'0-9]['.$chars.']*)', $m)) { - $parts[] = $m[1]; - if ($simple) break; - - while ($this->tagBracket($parts, $hasExpression)); - continue; + $this->eatWhiteDefault = $oldWhite; + if (!$parts) { + $this->seek($s); + return false; } - if (isset($this->buffer[$this->count]) && $this->buffer[$this->count] == "@") { - if ($this->interpolation($interp)) { - $hasExpression = true; - $interp[2] = true; // don't unescape - $parts[] = $interp; - continue; - } - - if ($this->literal("@")) { - $parts[] = "@"; - continue; - } + if ($hasExpression) { + $tag = array("exp", array("string", "", $parts)); + } else { + $tag = trim(implode($parts)); } - if ($this->unit($unit)) { // for keyframes - $parts[] = $unit[1]; - $parts[] = $unit[2]; - continue; - } - - break; + $this->whitespace(); + return true; } - $this->eatWhiteDefault = $oldWhite; - if (!$parts) { - $this->seek($s); - return false; - } - - if ($hasExpression) { - $tag = array("exp", array("string", "", $parts)); - } else { - $tag = trim(implode($parts)); - } - - $this->whitespace(); - return true; - } - // a css function - protected function func(&$func) - { - $s = $this->seek(); + protected function func(&$func) { + $s = $this->seek(); - if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) { - $fname = $m[1]; + if ($this->match('(%|[\w\-_][\w\-_:\.]+|[\w_])', $m) && $this->literal('(')) { + $fname = $m[1]; - $sPreArgs = $this->seek(); + $sPreArgs = $this->seek(); - $args = array(); - while (true) { - $ss = $this->seek(); - // this ugly nonsense is for ie filter properties - if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) { - $args[] = array("string", "", array($name, "=", $value)); - } else { - $this->seek($ss); - if ($this->expressionList($value)) { - $args[] = $value; + $args = array(); + while (true) { + $ss = $this->seek(); + // this ugly nonsense is for ie filter properties + if ($this->keyword($name) && $this->literal('=') && $this->expressionList($value)) { + $args[] = array("string", "", array($name, "=", $value)); + } else { + $this->seek($ss); + if ($this->expressionList($value)) { + $args[] = $value; + } + } + + if (!$this->literal(',')) break; + } + $args = array('list', ',', $args); + + if ($this->literal(')')) { + $func = array('function', $fname, $args); + return true; + } elseif ($fname == 'url') { + // couldn't parse and in url? treat as string + $this->seek($sPreArgs); + if ($this->openString(")", $string) && $this->literal(")")) { + $func = array('function', $fname, $string); + return true; } } - - if (!$this->literal(',')) break; } - $args = array('list', ',', $args); - if ($this->literal(')')) { - $func = array('function', $fname, $args); - return true; - } elseif ($fname == 'url') { - // couldn't parse and in url? treat as string - $this->seek($sPreArgs); - if ($this->openString(")", $string) && $this->literal(")")) { - $func = array('function', $fname, $string); - return true; - } - } - } - - $this->seek($s); - return false; - } - - // consume a less variable - protected function variable(&$name) - { - $s = $this->seek(); - if ($this->literal($this->lessc->vPrefix, false) && - ($this->variable($sub) || $this->keyword($name)) - ) { - if (!empty($sub)) { - $name = array('variable', $sub); - } else { - $name = $this->lessc->vPrefix.$name; - } - return true; - } - - $name = null; $this->seek($s); return false; - } + } + + // consume a less variable + protected function variable(&$name) { + $s = $this->seek(); + if ($this->literal($this->lessc->vPrefix, false) && + ($this->variable($sub) || $this->keyword($name)) + ) { + if (!empty($sub)) { + $name = array('variable', $sub); + } else { + $name = $this->lessc->vPrefix.$name; + } + return true; + } + + $name = null; + $this->seek($s); + return false; + } /** * Consume an assignment operator * Can optionally take a name that will be set to the current property name */ - protected function assign($name = null) - { - if ($name) $this->currentProperty = $name; - return $this->literal(':') || $this->literal('='); - } + protected function assign($name = null) { + if ($name) $this->currentProperty = $name; + return $this->literal(':') || $this->literal('='); + } // consume a keyword - protected function keyword(&$word) - { - if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) { - $word = $m[1]; - return true; + protected function keyword(&$word) { + if ($this->match('([\w_\-\*!"][\w\-_"]*)', $m)) { + $word = $m[1]; + return true; + } + return false; } - return false; - } // consume an end of statement delimiter - protected function end() - { - if ($this->literal(';', false)) { - return true; - } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') { - // if there is end of file or a closing block next then we don't need a ; - return true; - } - return false; - } - - protected function guards(&$guards) - { - $s = $this->seek(); - - if (!$this->literal("when")) { - $this->seek($s); + protected function end() { + if ($this->literal(';', false)) { + return true; + } elseif ($this->count == strlen($this->buffer) || $this->buffer[$this->count] == '}') { + // if there is end of file or a closing block next then we don't need a ; + return true; + } return false; } - $guards = array(); + protected function guards(&$guards) { + $s = $this->seek(); - while ($this->guardGroup($g)) { - $guards[] = $g; - if (!$this->literal(",")) break; + if (!$this->literal("when")) { + $this->seek($s); + return false; + } + + $guards = array(); + + while ($this->guardGroup($g)) { + $guards[] = $g; + if (!$this->literal(",")) break; + } + + if (count($guards) == 0) { + $guards = null; + $this->seek($s); + return false; + } + + return true; } - if (count($guards) == 0) { - $guards = null; - $this->seek($s); - return false; - } - - return true; - } - // a bunch of guards that are and'd together // TODO rename to guardGroup - protected function guardGroup(&$guardGroup) - { - $s = $this->seek(); - $guardGroup = array(); - while ($this->guard($guard)) { - $guardGroup[] = $guard; - if (!$this->literal("and")) break; - } + protected function guardGroup(&$guardGroup) { + $s = $this->seek(); + $guardGroup = array(); + while ($this->guard($guard)) { + $guardGroup[] = $guard; + if (!$this->literal("and")) break; + } - if (count($guardGroup) == 0) { - $guardGroup = null; - $this->seek($s); - return false; - } + if (count($guardGroup) == 0) { + $guardGroup = null; + $this->seek($s); + return false; + } - return true; - } - - protected function guard(&$guard) - { - $s = $this->seek(); - $negate = $this->literal("not"); - - if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { - $guard = $exp; - if ($negate) $guard = array("negate", $guard); return true; } - $this->seek($s); - return false; - } + protected function guard(&$guard) { + $s = $this->seek(); + $negate = $this->literal("not"); - /* raw parsing functions */ - - protected function literal($what, $eatWhitespace = null) - { - if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; - - // shortcut on single letter - if (!isset($what[1]) && isset($this->buffer[$this->count])) { - if ($this->buffer[$this->count] == $what) { - if (!$eatWhitespace) { - $this->count++; - return true; - } - // goes below... - } else { - return false; + if ($this->literal("(") && $this->expression($exp) && $this->literal(")")) { + $guard = $exp; + if ($negate) $guard = array("negate", $guard); + return true; } - } - if (!isset(self::$literalCache[$what])) { - self::$literalCache[$what] = lessc::preg_quote($what); - } - - return $this->match(self::$literalCache[$what], $m, $eatWhitespace); - } - - protected function genericList(&$out, $parseItem, $delim = "", $flatten = true) - { - $s = $this->seek(); - $items = array(); - while ($this->$parseItem($value)) { - $items[] = $value; - if ($delim) { - if (!$this->literal($delim)) break; - } - } - - if (count($items) == 0) { $this->seek($s); return false; } - if ($flatten && count($items) == 1) { - $out = $items[0]; - } else { - $out = array("list", $delim, $items); + /* raw parsing functions */ + + protected function literal($what, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + // shortcut on single letter + if (!isset($what[1]) && isset($this->buffer[$this->count])) { + if ($this->buffer[$this->count] == $what) { + if (!$eatWhitespace) { + $this->count++; + return true; + } + // goes below... + } else { + return false; + } + } + + if (!isset(self::$literalCache[$what])) { + self::$literalCache[$what] = lessc::preg_quote($what); + } + + return $this->match(self::$literalCache[$what], $m, $eatWhitespace); } - return true; - } + protected function genericList(&$out, $parseItem, $delim = "", $flatten = true) { + $s = $this->seek(); + $items = array(); + while ($this->$parseItem($value)) { + $items[] = $value; + if ($delim) { + if (!$this->literal($delim)) break; + } + } + + if (count($items) == 0) { + $this->seek($s); + return false; + } + + if ($flatten && count($items) == 1) { + $out = $items[0]; + } else { + $out = array("list", $delim, $items); + } + + return true; + } // advance counter to next occurrence of $what // $until - don't include $what in advance // $allowNewline, if string, will be used as valid char set - protected function to($what, &$out, $until = false, $allowNewline = false) - { - if (is_string($allowNewline)) { - $validChars = $allowNewline; - } else { - $validChars = $allowNewline ? "." : "[^\n]"; - } - if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false; - if ($until) $this->count -= strlen($what); // give back $what - $out = $m[1]; - return true; - } - - // try to match something on head of buffer - protected function match($regex, &$out, $eatWhitespace = null) - { - if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; - - $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais'; - if (preg_match($r, $this->buffer, $out, null, $this->count)) { - $this->count += strlen($out[0]); - if ($eatWhitespace && $this->writeComments) $this->whitespace(); + protected function to($what, &$out, $until = false, $allowNewline = false) { + if (is_string($allowNewline)) { + $validChars = $allowNewline; + } else { + $validChars = $allowNewline ? "." : "[^\n]"; + } + if (!$this->match('('.$validChars.'*?)'.lessc::preg_quote($what), $m, !$until)) return false; + if ($until) $this->count -= strlen($what); // give back $what + $out = $m[1]; return true; } - return false; - } + + // try to match something on head of buffer + protected function match($regex, &$out, $eatWhitespace = null) { + if ($eatWhitespace === null) $eatWhitespace = $this->eatWhiteDefault; + + $r = '/'.$regex.($eatWhitespace && !$this->writeComments ? '\s*' : '').'/Ais'; + if (preg_match($r, $this->buffer, $out, null, $this->count)) { + $this->count += strlen($out[0]); + if ($eatWhitespace && $this->writeComments) $this->whitespace(); + return true; + } + return false; + } // match some whitespace - protected function whitespace() - { - if ($this->writeComments) { - $gotWhite = false; - while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { - if (isset($m[1]) && empty($this->seenComments[$this->count])) { - $this->append(array("comment", $m[1])); - $this->seenComments[$this->count] = true; + protected function whitespace() { + if ($this->writeComments) { + $gotWhite = false; + while (preg_match(self::$whitePattern, $this->buffer, $m, null, $this->count)) { + if (isset($m[1]) && empty($this->seenComments[$this->count])) { + $this->append(array("comment", $m[1])); + $this->seenComments[$this->count] = true; + } + $this->count += strlen($m[0]); + $gotWhite = true; } - $this->count += strlen($m[0]); - $gotWhite = true; + return $gotWhite; + } else { + $this->match("", $m); + return strlen($m[0]) > 0; } - return $gotWhite; - } else { - $this->match("", $m); - return strlen($m[0]) > 0; } - } // match something without consuming it - protected function peek($regex, &$out = null, $from = null) - { - if (is_null($from)) $from = $this->count; - $r = '/'.$regex.'/Ais'; - $result = preg_match($r, $this->buffer, $out, null, $from); + protected function peek($regex, &$out = null, $from = null) { + if (is_null($from)) $from = $this->count; + $r = '/'.$regex.'/Ais'; + $result = preg_match($r, $this->buffer, $out, null, $from); - return $result; - } + return $result; + } // seek to a spot in the buffer or return where we are on no argument - protected function seek($where = null) - { - if ($where === null) return $this->count; - else $this->count = $where; - return true; - } + protected function seek($where = null) { + if ($where === null) return $this->count; + else $this->count = $where; + return true; + } /* misc functions */ - public function throwError($msg = "parse error", $count = null) - { - $count = is_null($count) ? $this->count : $count; + public function throwError($msg = "parse error", $count = null) { + $count = is_null($count) ? $this->count : $count; - $line = $this->line + - substr_count(substr($this->buffer, 0, $count), "\n"); + $line = $this->line + + substr_count(substr($this->buffer, 0, $count), "\n"); - if (!empty($this->sourceName)) { - $loc = "$this->sourceName on line $line"; - } else { - $loc = "line: $line"; + if (!empty($this->sourceName)) { + $loc = "$this->sourceName on line $line"; + } else { + $loc = "line: $line"; + } + + // TODO this depends on $this->count + if ($this->peek("(.*?)(\n|$)", $m, $count)) { + throw new exception("$msg: failed at `$m[1]` $loc"); + } else { + throw new exception("$msg: $loc"); + } } - // TODO this depends on $this->count - if ($this->peek("(.*?)(\n|$)", $m, $count)) { - throw new exception("$msg: failed at `$m[1]` $loc"); - } else { - throw new exception("$msg: $loc"); + protected function pushBlock($selectors = null, $type = null) { + $b = new stdclass; + $b->parent = $this->env; + + $b->type = $type; + $b->id = self::$nextBlockId++; + + $b->isVararg = false; // TODO: kill me from here + $b->tags = $selectors; + + $b->props = array(); + $b->children = array(); + + $this->env = $b; + return $b; } - } - - protected function pushBlock($selectors = null, $type = null) - { - $b = new stdclass; - $b->parent = $this->env; - - $b->type = $type; - $b->id = self::$nextBlockId++; - - $b->isVararg = false; // TODO: kill me from here - $b->tags = $selectors; - - $b->props = array(); - $b->children = array(); - - $this->env = $b; - return $b; - } // push a block that doesn't multiply tags - protected function pushSpecialBlock($type) - { - return $this->pushBlock(null, $type); - } + protected function pushSpecialBlock($type) { + return $this->pushBlock(null, $type); + } // append a property to the current block - protected function append($prop, $pos = null) - { - if ($pos !== null) $prop[-1] = $pos; - $this->env->props[] = $prop; - } + protected function append($prop, $pos = null) { + if ($pos !== null) $prop[-1] = $pos; + $this->env->props[] = $prop; + } // pop something off the stack - protected function pop() - { - $old = $this->env; - $this->env = $this->env->parent; - return $old; - } + protected function pop() { + $old = $this->env; + $this->env = $this->env->parent; + return $old; + } // remove comments from $text // todo: make it work for all functions, not just url - protected function removeComments($text) - { - $look = array( - 'url(', '//', '/*', '"', "'" - ); - - $out = ''; - $min = null; - while (true) { - // find the next item - foreach ($look as $token) { - $pos = strpos($text, $token); - if ($pos !== false) { - if (!isset($min) || $pos < $min[1]) $min = array($token, $pos); - } - } - - if (is_null($min)) break; - - $count = $min[1]; - $skip = 0; - $newlines = 0; - switch ($min[0]) { - case 'url(': - if (preg_match('/url\(.*?\)/', $text, $m, 0, $count)) - $count += strlen($m[0]) - strlen($min[0]); - break; - case '"': - case "'": - if (preg_match('/'.$min[0].'.*?(?indentLevel = 0; } - public function indentStr($n = 0) - { + public function indentStr($n = 0) { return str_repeat($this->indentChar, max($this->indentLevel + $n, 0)); } - public function property($name, $value) - { + public function property($name, $value) { return $name.$this->assignSeparator.$value.";"; } - protected function isEmpty($block) - { + protected function isEmpty($block) { if (empty($block->lines)) { foreach ($block->children as $child) { if (!$this->isEmpty($child)) return false; @@ -3991,8 +3818,7 @@ class lessc_formatter_classic return false; } - public function block($block) - { + public function block($block) { if ($this->isEmpty($block)) return; $inner = $pre = $this->indentStr(); @@ -4018,6 +3844,7 @@ class lessc_formatter_classic echo $this->open.$this->break; $inner = $this->indentStr(); } + } if (!empty($block->lines)) { @@ -4049,8 +3876,7 @@ class lessc_formatter_classic /** * Class for compressed result */ -class lessc_formatter_compressed extends lessc_formatter_classic -{ +class lessc_formatter_compressed extends lessc_formatter_classic { public $disableSingle = true; public $open = "{"; public $selectorSeparator = ","; @@ -4058,8 +3884,7 @@ class lessc_formatter_compressed extends lessc_formatter_classic public $break = ""; public $compressColors = true; - public function indentStr($n = 0) - { + public function indentStr($n = 0) { return ""; } } @@ -4067,8 +3892,7 @@ class lessc_formatter_compressed extends lessc_formatter_classic /** * Class for lessjs */ -class lessc_formatter_lessjs extends lessc_formatter_classic -{ +class lessc_formatter_lessjs extends lessc_formatter_classic { public $disableSingle = true; public $breakSelectors = true; public $assignSeparator = ": "; diff --git a/htdocs/core/lib/security2.lib.php b/htdocs/core/lib/security2.lib.php index ab3f3c3f4c8..691af9dc5bd 100644 --- a/htdocs/core/lib/security2.lib.php +++ b/htdocs/core/lib/security2.lib.php @@ -448,15 +448,15 @@ function getRandomPassword($generic = false, $replaceambiguouschars = null, $len { $max = strlen($lowercase) - 1; for ($x = 0; $x < $nbofchar; $x++) { - $randomCode .= $lowercase{random_int(0, $max)}; + $randomCode .= $lowercase[random_int(0, $max)]; } $max = strlen($uppercase) - 1; for ($x = 0; $x < $nbofchar; $x++) { - $randomCode .= $uppercase{random_int(0, $max)}; + $randomCode .= $uppercase[random_int(0, $max)]; } $max = strlen($numbers) - 1; for ($x = 0; $x < $nbofcharlast; $x++) { - $randomCode .= $numbers{random_int(0, $max)}; + $randomCode .= $numbers[random_int(0, $max)]; } $generated_password = str_shuffle($randomCode); @@ -464,15 +464,15 @@ function getRandomPassword($generic = false, $replaceambiguouschars = null, $len { $max = strlen($lowercase) - 1; for ($x = 0; $x < $nbofchar; $x++) { - $randomCode .= $lowercase{mt_rand(0, $max)}; + $randomCode .= $lowercase[mt_rand(0, $max)]; } $max = strlen($uppercase) - 1; for ($x = 0; $x < $nbofchar; $x++) { - $randomCode .= $uppercase{mt_rand(0, $max)}; + $randomCode .= $uppercase[mt_rand(0, $max)]; } $max = strlen($numbers) - 1; for ($x = 0; $x < $nbofcharlast; $x++) { - $randomCode .= $numbers{mt_rand(0, $max)}; + $randomCode .= $numbers[mt_rand(0, $max)]; } $generated_password = str_shuffle($randomCode); @@ -495,9 +495,9 @@ function getRandomPassword($generic = false, $replaceambiguouschars = null, $len $max = strlen($numbers) - 1; if (function_exists('random_int')) // Cryptographic random { - $generated_password = str_replace($replaceambiguouschars, $numbers{random_int(0, $max)}, $generated_password); + $generated_password = str_replace($replaceambiguouschars, $numbers[random_int(0, $max)], $generated_password); } else { - $generated_password = str_replace($replaceambiguouschars, $numbers{mt_rand(0, $max)}, $generated_password); + $generated_password = str_replace($replaceambiguouschars, $numbers[mt_rand(0, $max)], $generated_password); } } diff --git a/htdocs/core/lib/website2.lib.php b/htdocs/core/lib/website2.lib.php index 980326d41be..dbdeecc72e8 100644 --- a/htdocs/core/lib/website2.lib.php +++ b/htdocs/core/lib/website2.lib.php @@ -228,11 +228,11 @@ function dolSavePageContent($filetpl, Website $object, WebsitePage $objectpage) /** - * Save content of the index.php and wrapper.php page + * Save content of the index.php and/or wrapper.php page * * @param string $pathofwebsite Path of website root * @param string $fileindex Full path of file index.php - * @param string $filetpl File tpl to index.php page redirect to + * @param string $filetpl File tpl the index.php page redirect to * @param string $filewrapper Full path of file wrapper.php * @return boolean True if OK */ @@ -245,29 +245,39 @@ function dolSaveIndexPage($pathofwebsite, $fileindex, $filetpl, $filewrapper) dol_mkdir($pathofwebsite); - dol_delete_file($fileindex); - $indexcontent = ''."\n"; - $result1 = file_put_contents($fileindex, $indexcontent); - if (!empty($conf->global->MAIN_UMASK)) { - @chmod($fileindex, octdec($conf->global->MAIN_UMASK)); + if ($fileindex) { + dol_delete_file($fileindex); + $indexcontent = ''."\n"; + + $result1 = file_put_contents($fileindex, $indexcontent); + if (!empty($conf->global->MAIN_UMASK)) { + @chmod($fileindex, octdec($conf->global->MAIN_UMASK)); + } + } + else { + $result1 = true; } - dol_delete_file($filewrapper); - $wrappercontent = file_get_contents(DOL_DOCUMENT_ROOT.'/website/samples/wrapper.php'); + if ($filewrapper) { + dol_delete_file($filewrapper); + $wrappercontent = file_get_contents(DOL_DOCUMENT_ROOT.'/website/samples/wrapper.php'); - $result2 = file_put_contents($filewrapper, $wrappercontent); - if (!empty($conf->global->MAIN_UMASK)) { - @chmod($filewrapper, octdec($conf->global->MAIN_UMASK)); + $result2 = file_put_contents($filewrapper, $wrappercontent); + if (!empty($conf->global->MAIN_UMASK)) { + @chmod($filewrapper, octdec($conf->global->MAIN_UMASK)); + } + } else { + $result2 = true; } return ($result1 && $result2); diff --git a/htdocs/core/lib/xcal.lib.php b/htdocs/core/lib/xcal.lib.php index fd563b3deef..e7252ac2171 100644 --- a/htdocs/core/lib/xcal.lib.php +++ b/htdocs/core/lib/xcal.lib.php @@ -330,9 +330,10 @@ function build_calfile($format, $title, $desc, $events_array, $outputfile) * @param string $outputfile Output file * @param string $filter (optional) Filter * @param string $url Url (If empty, forge URL for agenda RSS export) + * @param string $langcode Language code to show in header * @return int < 0 if ko, Nb of events in file if ok */ -function build_rssfile($format, $title, $desc, $events_array, $outputfile, $filter = '', $url = '') +function build_rssfile($format, $title, $desc, $events_array, $outputfile, $filter = '', $url = '', $langcode = '') { global $user, $conf, $langs; global $dolibarr_main_url_root; @@ -358,7 +359,9 @@ function build_rssfile($format, $title, $desc, $events_array, $outputfile, $filt fwrite($fichier, ''); fwrite($fichier, "\n"); - fwrite($fichier, "\n".$title."\n"); + fwrite($fichier, "\n"); + fwrite($fichier, "".$title."\n"); + if ($langcode) fwrite($fichier, "".$langcode."\n"); /* fwrite($fichier, ""."\n". diff --git a/htdocs/datapolicy/class/datapolicycron.class.php b/htdocs/datapolicy/class/datapolicycron.class.php index 40446392596..280f900f307 100644 --- a/htdocs/datapolicy/class/datapolicycron.class.php +++ b/htdocs/datapolicy/class/datapolicycron.class.php @@ -1,6 +1,6 @@ - * Copyright (C) 2018 Frédéric France + * Copyright (C) 2018-2020 Frédéric France * * 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 @@ -482,7 +482,7 @@ class DataPolicyCron while ($i < $num && !$error) { - $obj = $db->fetch_object($resql); + $obj = $this->db->fetch_object($resql); $object->fetch($obj->rowid); $object->id = $obj->rowid; diff --git a/htdocs/fourn/commande/dispatch.php b/htdocs/fourn/commande/dispatch.php index f867fc51c3a..7f99b2697af 100644 --- a/htdocs/fourn/commande/dispatch.php +++ b/htdocs/fourn/commande/dispatch.php @@ -8,7 +8,7 @@ * Copyright (C) 2016 Florian Henry * Copyright (C) 2017 Ferran Marcet * Copyright (C) 2018 Frédéric France - * Copyright (C) 2019 Christophe Battarel + * Copyright (C) 2019-2020 Christophe Battarel * * 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 @@ -38,6 +38,8 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/fourn.lib.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.class.php'; require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.commande.dispatch.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; +require_once DOL_DOCUMENT_ROOT . '/product/stock/class/mouvementstock.class.php'; + if (!empty($conf->projet->enabled)) require_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; @@ -53,6 +55,8 @@ $ref = GETPOST('ref'); $lineid = GETPOST('lineid', 'int'); $action = GETPOST('action', 'aZ09'); $fk_default_warehouse = GETPOST('fk_default_warehouse', 'int'); +$cancel = GETPOST('cancel', 'alpha'); +$confirm = GETPOST('confirm', 'alpha'); if ($user->socid) $socid = $user->socid; @@ -360,6 +364,126 @@ if ($action == 'dispatch' && $user->rights->fournisseur->commande->receptionner) } } +// Remove a dispatched line +if ($action == 'confirm_deleteline' && $confirm == 'yes' && $user->rights->fournisseur->commande->receptionner) +{ + $db->begin(); + + $supplierorderdispatch = new CommandeFournisseurDispatch($db); + $result = $supplierorderdispatch->fetch($lineid); + if ($result > 0) + { + $qty = $supplierorderdispatch->qty; + $entrepot = $supplierorderdispatch->fk_entrepot; + $product = $supplierorderdispatch->fk_product; + $price = GETPOST('price'); + $comment = $supplierorderdispatch->comment; + $eatby = $supplierorderdispatch->fk_product; + $sellby = $supplierorderdispatch->sellby; + $batch = $supplierorderdispatch->batch; + + $result = $supplierorderdispatch->delete($user); + } + if ($result < 0) + { + $errors = $object->errors; + $error++; + } + else + { + // If module stock is enabled and the stock increase is done on purchase order dispatching + if ($entrepot > 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) + { + $mouv = new MouvementStock($db); + if ($product > 0) + { + $mouv->origin = &$object; + $result=$mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch); + if ($result < 0) + { + $errors=$mouv->errors; + $error++; + } + } + } + } + if ($error > 0) + { + $db->rollback(); + setEventMessages($error, $errors, 'errors'); + } + else + { + $db->commit(); + } +} + +// Update a dispatched line +if ($action == 'updateline' && $user->rights->fournisseur->commande->receptionner) +{ + $db->begin(); + $error = 0; + + $supplierorderdispatch = new CommandeFournisseurDispatch($db); + $result = $supplierorderdispatch->fetch($lineid); + if ($result > 0) + { + $qty = $supplierorderdispatch->qty; + $entrepot = $supplierorderdispatch->fk_entrepot; + $product = $supplierorderdispatch->fk_product; + $price = GETPOST('price'); + $comment = $supplierorderdispatch->comment; + $eatby = $supplierorderdispatch->fk_product; + $sellby = $supplierorderdispatch->sellby; + $batch = $supplierorderdispatch->batch; + + $supplierorderdispatch->qty = GETPOST('qty', 'int'); + $supplierorderdispatch->fk_entrepot = GETPOST('fk_entrepot'); + $result = $supplierorderdispatch->update($user); + } + if ($result < 0) + { + $error++; + $errors=$supplierorderdispatch->errors; + } + else + { + // If module stock is enabled and the stock increase is done on purchase order dispatching + if ($entrepot > 0 && ! empty($conf->stock->enabled) && ! empty($conf->global->STOCK_CALCULATE_ON_SUPPLIER_DISPATCH_ORDER)) + { + $mouv = new MouvementStock($db); + if ($product > 0) + { + $mouv->origin = &$object; + $result=$mouv->livraison($user, $product, $entrepot, $qty, $price, $comment, '', $eatby, $sellby, $batch); + if ($result < 0) + { + $errors=$mouv->errors; + $error++; + } + else + { + $mouv->origin = &$object; + $result=$mouv->reception($user, $product, $supplierorderdispatch->fk_entrepot, $supplierorderdispatch->qty, $price, $comment, $eatby, $sellby, $batch); + if ($result < 0) + { + $errors=$mouv->errors; + $error++; + } + } + } + } + } + if ($error > 0) + { + $db->rollback(); + setEventMessages($error, $errors, 'errors'); + } + else + { + $db->commit(); + } +} /* * View @@ -373,7 +497,7 @@ $warehouse_static = new Entrepot($db); $supplierorderdispatch = new CommandeFournisseurDispatch($db); $help_url = 'EN:Module_Suppliers_Orders|FR:CommandeFournisseur|ES:Módulo_Pedidos_a_proveedores'; -llxHeader('', $langs->trans("Order"), $help_url, '', 0, 0, array('/fourn/js/lib_dispatch.js.php')); +llxHeader('', $langs->trans("OrderDispatch"), $help_url, '', 0, 0, array('/fourn/js/lib_dispatch.js.php')); if ($id > 0 || !empty($ref)) { $soc = new Societe($db); @@ -387,6 +511,23 @@ if ($id > 0 || !empty($ref)) { $title = $langs->trans("SupplierOrder"); dol_fiche_head($head, 'dispatch', $title, -1, 'order'); + $formconfirm=''; + + // Confirmation to delete line + if ($action == 'ask_deleteline') + { + $formconfirm=$form->formconfirm($_SERVER["PHP_SELF"].'?id='.$object->id.'&lineid='.$lineid, $langs->trans('DeleteLine'), $langs->trans('ConfirmDeleteLine'), 'confirm_deleteline', '', 0, 1); + } + + // Call Hook formConfirm + $parameters = array('lineid' => $lineid); + // Note that $action and $object may be modified by hook + $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); + if (empty($reshook)) $formconfirm.=$hookmanager->resPrint; + elseif ($reshook > 0) $formconfirm=$hookmanager->resPrint; + + // Print form confirm + print $formconfirm; // Supplier order card @@ -487,6 +628,9 @@ if ($id > 0 || !empty($ref)) { require_once DOL_DOCUMENT_ROOT.'/product/class/html.formproduct.class.php'; $formproduct = new FormProduct($db); $formproduct->loadWarehouses(); + $entrepot = new Entrepot($db); + $listwarehouses=$entrepot->list_array(1); + if (empty($conf->reception->enabled))print '
'; else print ''; @@ -561,8 +705,6 @@ if ($id > 0 || !empty($ref)) { $i = 0; if ($num) { - $entrepot = new Entrepot($db); - $listwarehouses = $entrepot->list_array(1); print ''; @@ -915,9 +1057,11 @@ if ($id > 0 || !empty($ref)) { $sql = "SELECT p.ref, p.label,"; $sql .= " e.rowid as warehouse_id, e.ref as entrepot,"; $sql .= " cfd.rowid as dispatchlineid, cfd.fk_product, cfd.qty, cfd.eatby, cfd.sellby, cfd.batch, cfd.comment, cfd.status, cfd.datec"; + $sql.=" ,cd.rowid, cd.subprice"; if ($conf->reception->enabled)$sql .= " ,cfd.fk_reception, r.date_delivery"; $sql .= " FROM ".MAIN_DB_PREFIX."product as p,"; $sql .= " ".MAIN_DB_PREFIX."commande_fournisseur_dispatch as cfd"; + $sql .= " LEFT JOIN " . MAIN_DB_PREFIX . "commande_fournisseurdet as cd ON cd.rowid = cfd.fk_commandefourndet"; $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."entrepot as e ON cfd.fk_entrepot = e.rowid"; if ($conf->reception->enabled)$sql .= " LEFT JOIN ".MAIN_DB_PREFIX."reception as r ON cfd.fk_reception = r.rowid"; $sql .= " WHERE cfd.fk_commande = ".$object->id; @@ -949,7 +1093,6 @@ if ($id > 0 || !empty($ref)) { print ''.$langs->trans("SellByDate").''; } print ''.$langs->trans("QtyDispatched").''; - print ''; print ''.$langs->trans("Warehouse").''; print ''.$langs->trans("Comment").''; @@ -960,14 +1103,23 @@ if ($id > 0 || !empty($ref)) { print ''; } - print ''; + print ''; print "\n"; while ($i < $num) { $objp = $db->fetch_object($resql); - print ""; + if ($action == 'editline' && $lineid == $objp->dispatchlineid) + { + print ' + + + + '; + } + + print ''; if (!empty($conf->reception->enabled)) { print ''; @@ -994,14 +1146,37 @@ if ($id > 0 || !empty($ref)) { } // Qty - print ''.$objp->qty.''; - print ' '; + print ''; + if ($action == 'editline' && $lineid == $objp->dispatchlineid) + { + print ''; + } + else + { + print $objp->qty; + } + print ''; + print ''; // Warehouse print ''; - $warehouse_static->id = $objp->warehouse_id; - $warehouse_static->libelle = $objp->entrepot; - print $warehouse_static->getNomUrl(1); + if ($action == 'editline' && $lineid == $objp->dispatchlineid) + { + if (count($listwarehouses) > 1) { + print $formproduct->selectWarehouses(GETPOST("fk_entrepot")?GETPOST("fk_entrepot"):($objp->warehouse_id?$objp->warehouse_id:''), "fk_entrepot", '', 1, 0, $objp->fk_product, '', 1, 1, null, 'csswarehouse'); + } elseif (count($listwarehouses) == 1) { + print $formproduct->selectWarehouses(GETPOST("fk_entrepot")?GETPOST("fk_entrepot"):($objp->warehouse_id?$objp->warehouse_id:''), "fk_entrepot", '', 0, 0, $objp->fk_product, '', 1, 1, null, 'csswarehouse'); + } else { + $langs->load("errors"); + print $langs->trans("ErrorNoWarehouseDefined"); + } + } + else + { + $warehouse_static->id = $objp->warehouse_id; + $warehouse_static->libelle = $objp->entrepot; + print $warehouse_static->getNomUrl(1); + } print ''; // Comment @@ -1052,9 +1227,33 @@ if ($id > 0 || !empty($ref)) { print ''; } - print ''; + if ($action != 'editline' ||  && $lineid != $objp->dispatchlineid) + { + print ''; + print 'dispatchlineid .'#line_'. $objp->dispatchlineid . '">'; + print img_edit(); + print ''; + print ''; + + print ''; + print 'dispatchlineid . '#dispatch_received_products">'; + print img_delete(); + print ''; + print ''; + } + else + { + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + } + print "\n"; + if ($action == 'editline' && $lineid == $objp->dispatchlineid) print '
'; $i++; } diff --git a/htdocs/fourn/facture/card.php b/htdocs/fourn/facture/card.php index 349cf9571ab..04c55b6dae4 100644 --- a/htdocs/fourn/facture/card.php +++ b/htdocs/fourn/facture/card.php @@ -621,7 +621,7 @@ if (empty($reshook)) $ret = $extrafields->setOptionalsFromPost(null, $object); if ($ret < 0) $error++; - $datefacture = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $datefacture = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); $datedue = dol_mktime(12, 0, 0, $_POST['echmonth'], $_POST['echday'], $_POST['echyear']); // Replacement invoice @@ -1695,7 +1695,7 @@ if ($action == 'create') if (!empty($conf->global->MULTICURRENCY_USE_ORIGIN_TX) && !empty($objectsrc->multicurrency_tx)) $currency_tx = $objectsrc->multicurrency_tx; } - $datetmp = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $datetmp = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); $dateinvoice = ($datetmp == '' ? (empty($conf->global->MAIN_AUTOFILL_DATE) ?-1 : '') : $datetmp); $datetmp = dol_mktime(12, 0, 0, $_POST['echmonth'], $_POST['echday'], $_POST['echyear']); $datedue = ($datetmp == '' ?-1 : $datetmp); @@ -1707,7 +1707,7 @@ if ($action == 'create') $cond_reglement_id = $societe->cond_reglement_supplier_id; $mode_reglement_id = $societe->mode_reglement_supplier_id; $fk_account = $societe->fk_account; - $datetmp = dol_mktime(12, 0, 0, $_POST['remonth'], $_POST['reday'], $_POST['reyear']); + $datetmp = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); $dateinvoice = ($datetmp == '' ? (empty($conf->global->MAIN_AUTOFILL_DATE) ?-1 : '') : $datetmp); $datetmp = dol_mktime(12, 0, 0, $_POST['echmonth'], $_POST['echday'], $_POST['echyear']); $datedue = ($datetmp == '' ?-1 : $datetmp); @@ -1715,6 +1715,18 @@ if ($action == 'create') if (!empty($conf->multicurrency->enabled) && !empty($soc->multicurrency_code)) $currency_code = $soc->multicurrency_code; } + // when payment condition is empty (means not override by payment condition form a other object, like third-party), try to use default value + if(empty($cond_reglement_id)) + { + $cond_reglement_id = GETPOST("cond_reglement_id"); + } + + // when payment mode is empty (means not override by payment condition form a other object, like third-party), try to use default value + if(empty($mode_reglement_id)) + { + $mode_reglement_id = GETPOST("mode_reglement_id"); + } + print '
'; print ''; print ''; diff --git a/htdocs/langs/en_US/agenda.lang b/htdocs/langs/en_US/agenda.lang index 6463b4eb9a0..bb2ba38b297 100644 --- a/htdocs/langs/en_US/agenda.lang +++ b/htdocs/langs/en_US/agenda.lang @@ -154,3 +154,6 @@ EveryMonth=Every month DayOfMonth=Day of month DayOfWeek=Day of week DateStartPlusOne=Date start + 1 hour +SetAllEventsToTodo=Set all events to todo +SetAllEventsToInProgress=Set all events to in progress +SetAllEventsToFinished=Set all events to finished diff --git a/htdocs/langs/en_US/mrp.lang b/htdocs/langs/en_US/mrp.lang index d3c4d3253c6..faa8eb60cf4 100644 --- a/htdocs/langs/en_US/mrp.lang +++ b/htdocs/langs/en_US/mrp.lang @@ -74,3 +74,4 @@ ProductsToConsume=Products to consume ProductsToProduce=Products to produce UnitCost=Unit cost TotalCost=Total cost +BOMTotalCost=The cost to produce this BOM based on cost of each quantity and product to consume (use Cost price if defined, else Average Weighted Price if defined, else the Best purchase price) \ No newline at end of file diff --git a/htdocs/langs/en_US/website.lang b/htdocs/langs/en_US/website.lang index 2c555b40c20..6109f3ca879 100644 --- a/htdocs/langs/en_US/website.lang +++ b/htdocs/langs/en_US/website.lang @@ -127,4 +127,6 @@ OtherLanguages=Other languages UseManifest=Provide a manifest.json file PublicAuthorAlias=Public author alias AvailableLanguagesAreDefinedIntoWebsiteProperties=Available languages are defined into website properties -ReplacementDoneInXPages=Replacement done in %s pages or containers \ No newline at end of file +ReplacementDoneInXPages=Replacement done in %s pages or containers +RSSFeed=RSS Feed +RSSFeedDesc=You can get a RSS feed of latest articles with type 'blogpost' using this URL \ No newline at end of file diff --git a/htdocs/loan/class/paymentloan.class.php b/htdocs/loan/class/paymentloan.class.php index 40479677fd0..84aca12394d 100644 --- a/htdocs/loan/class/paymentloan.class.php +++ b/htdocs/loan/class/paymentloan.class.php @@ -1,6 +1,6 @@ - * Copyright (C) 2015-2018 Frederic France + * Copyright (C) 2015-2020 Frederic France * * 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 @@ -40,6 +40,11 @@ class PaymentLoan extends CommonObject */ public $table_element = 'payment_loan'; + /** + * @var string String with name of icon for PaymentLoan + */ + public $picto = 'money-bill-alt'; + /** * @var int Loan ID */ diff --git a/htdocs/variants/class/ProductCombination.class.php b/htdocs/variants/class/ProductCombination.class.php index f19c81960db..118953c25ad 100644 --- a/htdocs/variants/class/ProductCombination.class.php +++ b/htdocs/variants/class/ProductCombination.class.php @@ -326,11 +326,20 @@ class ProductCombination $child = new Product($this->db); $child->fetch($this->fk_product_child); - $child->price_autogen = $parent->price_autogen; - $child->weight = $parent->weight + $this->variation_weight; - $child->weight_units = $parent->weight_units; - $varlabel = $this->getCombinationLabel($this->fk_product_child); - $child->label = $parent->label.$varlabel; + + $child->price_autogen = $parent->price_autogen; + $child->weight = $parent->weight; + if ($this->variation_weight) { // If we must add a delta on weight + $child->weight = ($child->weight ? $child->weight : 0) + $this->variation_weight; + } + $child->weight_units = $parent->weight_units; + + // Don't update the child label if the user has already modified it. + if ($child->label == $parent->label) { + // This will trigger only at variant creation time + $varlabel = $this->getCombinationLabel($this->fk_product_child); + $child->label = $parent->label.$varlabel;; + } if ($child->update($child->id, $user) > 0) { $new_vat = $parent->tva_tx; diff --git a/htdocs/website/index.php b/htdocs/website/index.php index a089e08282e..9e82359ffa3 100644 --- a/htdocs/website/index.php +++ b/htdocs/website/index.php @@ -196,6 +196,7 @@ $fileindex = $pathofwebsite.'/index.php'; $filewrapper = $pathofwebsite.'/wrapper.php'; $filemanifestjson = $pathofwebsite.'/manifest.json.php'; $filereadme = $pathofwebsite.'/README.md'; +$filemaster = $pathofwebsite.'/master.inc.php'; // Define $urlwithroot $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); @@ -1177,8 +1178,6 @@ if ($action == 'updatecss') if (!$error) { // Save master.inc.php file - $filemaster = $pathofwebsite.'/master.inc.php'; - dol_syslog("Save master file ".$filemaster); dol_mkdir($pathofwebsite); @@ -1372,6 +1371,10 @@ if ($action == 'updatecss') } + // Save wrapper.php + $result = dolSaveIndexPage($pathofwebsite, '', '', $filewrapper); + + // Message if no error if (!$error) { @@ -1704,10 +1707,9 @@ if (($action == 'updatesource' || $action == 'updatecontent' || $action == 'conf $action = 'createpagefromclone'; $db->rollback(); - } - else { - $fileindex = $pathofwebsitenew.'/index.php'; + } else { $filetpl = $pathofwebsitenew.'/page'.$resultpage->id.'.tpl.php'; + $fileindex = $pathofwebsitenew.'/index.php'; $filewrapper = $pathofwebsitenew.'/wrapper.php'; //var_dump($pathofwebsitenew); @@ -2922,6 +2924,14 @@ if ($action == 'editcss') print ''; + // RSS + print ''; + $htmlhelp = $langs->trans('RSSFeedDesc'); + print $form->textwithpicto($langs->trans('RSSFeed'), $htmlhelp, 1, 'help', '', 0, 2, ''); + print ''; + print '/wrapper.php?rss=1[&l=XX][&limit=123]'; + print ''; + print ''; dol_fiche_end(); diff --git a/htdocs/website/samples/wrapper.php b/htdocs/website/samples/wrapper.php index 1f6885c98f8..a04ff14882c 100644 --- a/htdocs/website/samples/wrapper.php +++ b/htdocs/website/samples/wrapper.php @@ -12,6 +12,8 @@ $hashp = GETPOST('hashp', 'aZ09'); $modulepart = GETPOST('modulepart', 'aZ09'); $entity = GETPOST('entity', 'int') ?GETPOST('entity', 'int') : $conf->entity; $original_file = GETPOST("file", "alpha"); +$l = GETPOST('l', 'aZ09'); +$limit = GETPOST('limit', 'int'); // Parameters for RSS $rss = GETPOST('rss', 'aZ09'); @@ -87,8 +89,8 @@ if ($rss) { $type = ''; $cachedelay = 0; $filename = $original_file; - $filters = array('type_container'=>'blogpost', 'lang'=>'en_US'); $dir_temp = $conf->website->dir_temp; + include_once DOL_DOCUMENT_ROOT.'/website/class/website.class.php'; include_once DOL_DOCUMENT_ROOT.'/website/class/websitepage.class.php'; $website = new Website($db); @@ -96,7 +98,10 @@ if ($rss) { $website->fetch('', $websitekey); - $MAXNEWS = 20; + $filters = array('type_container'=>'blogpost'); + if ($l) $filters['lang'] = $l; + + $MAXNEWS = ($limit ? $limit : 20); $arrayofblogs = $websitepage->fetchAll($website->id, 'DESC', 'date_creation', $MAXNEWS, 0, $filters); $eventarray = array(); if (is_array($arrayofblogs)) { @@ -148,7 +153,7 @@ if ($rss) { @chmod($outputfiletmp, octdec($conf->global->MAIN_UMASK)); // Write file - $result = build_rssfile($format, $title, $desc, $eventarray, $outputfiletmp, '', $website->virtualhost.'/wrapper.php?rss=1'); + $result = build_rssfile($format, $title, $desc, $eventarray, $outputfiletmp, '', $website->virtualhost.'/wrapper.php?rss=1'.($l ? '&l='.$l : ''), $l); if ($result >= 0) { diff --git a/test/phpunit/AccountingAccountTest.php b/test/phpunit/AccountingAccountTest.php index b4367e96f49..622bd346872 100644 --- a/test/phpunit/AccountingAccountTest.php +++ b/test/phpunit/AccountingAccountTest.php @@ -204,7 +204,7 @@ class AccountingAccountTest extends PHPUnit\Framework\TestCase $localobject->label='New label'; $result=$localobject->update($user); - print __METHOD__." id=".$id." result=".$result."\n"; + print __METHOD__." id=".$localobject->id." result=".$result."\n"; $this->assertLessThan($result, 0); return $localobject->id; diff --git a/test/phpunit/ActionCommTest.php b/test/phpunit/ActionCommTest.php index 0dd4946f69c..abebfd728bf 100644 --- a/test/phpunit/ActionCommTest.php +++ b/test/phpunit/ActionCommTest.php @@ -223,7 +223,7 @@ class ActionCommTest extends PHPUnit\Framework\TestCase $result=$localobject->update($user); $this->assertLessThan($result, 0); - print __METHOD__." id=".$id." result=".$result."\n"; + print __METHOD__." id=".$localobject->id." result=".$result."\n"; return $localobject->id; } diff --git a/test/phpunit/CommandeTest.php b/test/phpunit/CommandeTest.php index 990ef41f0b7..1decbe4e201 100644 --- a/test/phpunit/CommandeTest.php +++ b/test/phpunit/CommandeTest.php @@ -196,7 +196,7 @@ class CommandeTest extends PHPUnit\Framework\TestCase $result=$localobject->update($user); $this->assertLessThan($result, 0); - print __METHOD__." id=".$id." result=".$result."\n"; + print __METHOD__." id=".$localobject->id." result=".$result."\n"; return $localobject; }