function buildSortSQL($sortArray)
 {
     $sortCount = 0;
     $sortList = false;
     if (isset($sortArray) and is_array($sortArray) and count($sortArray) > 0) {
         $sortList = $sortArray;
         if (count($sortList) > 1 and !is_array($sortList[0])) {
             $sortList = array($sortList);
         }
     }
     $attributeJoinCount = 0;
     $attributeFromSQL = "";
     $attributeWereSQL = "";
     $selectSQL = '';
     if ($sortList !== false) {
         $sortingFields = '';
         foreach ($sortList as $sortBy) {
             if (is_array($sortBy) and count($sortBy) > 0) {
                 if ($sortCount > 0) {
                     $sortingFields .= ', ';
                 }
                 $sortField = $sortBy[0];
                 switch ($sortField) {
                     case 'path':
                         $sortingFields .= 'path_string';
                         break;
                     case 'published':
                         $sortingFields .= 'ezcontentobject.published';
                         break;
                     case 'modified':
                         $sortingFields .= 'ezcontentobject.modified';
                         break;
                     case 'section':
                         $sortingFields .= 'ezcontentobject.section_id';
                         break;
                     case 'depth':
                         $sortingFields .= 'depth';
                         break;
                     case 'class_identifier':
                         $sortingFields .= 'ezcontentclass.identifier';
                         $selectSQL .= ', ezcontentclass.identifier';
                         break;
                     case 'class_name':
                         $classNameFilter = eZContentClassName::sqlFilter();
                         $selectSQL .= ", " . $classNameFilter['nameField'] . " AS class_name";
                         $sortingFields .= "class_name";
                         $attributeFromSQL .= " INNER JOIN {$classNameFilter['from']} ON ({$classNameFilter['where']})";
                         break;
                     case 'priority':
                         $sortingFields .= 'ezcontentobject_tree.priority';
                         break;
                     case 'name':
                         $sortingFields .= 'ezcontentobject_name.name';
                         break;
                     case 'attribute':
                         $sortClassID = $sortBy[2];
                         // Look up datatype for sorting
                         if (!is_numeric($sortClassID)) {
                             $sortClassID = eZContentObjectTreeNode::classAttributeIDByIdentifier($sortClassID);
                         }
                         $sortDataType = $sortClassID === false ? false : eZContentObjectTreeNode::sortKeyByClassAttributeID($sortClassID);
                         $sortKey = false;
                         if ($sortDataType == 'string') {
                             $sortKey = 'sort_key_string';
                         } else {
                             $sortKey = 'sort_key_int';
                         }
                         $sortingFields .= "a{$attributeJoinCount}.{$sortKey}";
                         $attributeFromSQL .= " INNER JOIN ezcontentobject_attribute as a{$attributeJoinCount} ON (a{$attributeJoinCount}.contentobject_id = ezcontentobject.id AND a{$attributeJoinCount}.version = ezcontentobject_name.content_version)";
                         $attributeWereSQL .= " AND a{$attributeJoinCount}.contentclassattribute_id = {$sortClassID}";
                         $selectSQL .= ", a{$attributeJoinCount}.{$sortKey}";
                         $attributeJoinCount++;
                         break;
                     default:
                         eZDebug::writeWarning('Unknown sort field: ' . $sortField, __METHOD__);
                         continue;
                 }
                 $sortOrder = true;
                 // true is ascending
                 if (isset($sortBy[1])) {
                     $sortOrder = $sortBy[1];
                 }
                 $sortingFields .= $sortOrder ? " ASC" : " DESC";
                 ++$sortCount;
             }
         }
     }
     // Should we sort?
     if ($sortCount == 0) {
         $sortingFields = " ezcontentobject.published ASC";
     }
     return array('sortingFields' => $sortingFields, 'selectSQL' => $selectSQL, 'fromSQL' => $attributeFromSQL, 'whereSQL' => $attributeWereSQL);
 }
 /**
  * Returns an array to filter a query by the given attributes in $attributeFilter
  *
  * @param array|bool $attributeFilter
  * @param array $sortingInfo
  * @param array|bool $language
  * @return array|bool
  */
 static function createAttributeFilterSQLStrings(&$attributeFilter, &$sortingInfo = array('sortCount' => 0, 'attributeJoinCount' => 0), $language = false)
 {
     // Check for attribute filtering
     $filterSQL = array('from' => '', 'where' => '');
     if ($language !== false && !is_array($language)) {
         $language = array($language);
     }
     $totalAttributesFiltersCount = 0;
     $invalidAttributesFiltersCount = 0;
     if (isset($attributeFilter) && $attributeFilter !== false) {
         if (!is_array($attributeFilter)) {
             eZDebug::writeError("\$attributeFilter needs to be an array", __METHOD__);
             return $filterSQL;
         }
         $filterArray = $attributeFilter;
         // Check if first value of array is a string.
         // To check for and/or filtering
         $filterJoinType = 'AND';
         if (is_string($filterArray[0])) {
             if (strtolower($filterArray[0]) == 'or') {
                 $filterJoinType = 'OR';
             } else {
                 if (strtolower($filterArray[0]) == 'and') {
                     $filterJoinType = 'AND';
                 }
             }
             unset($filterArray[0]);
         }
         $attibuteFilterJoinSQL = "";
         $filterCount = $sortingInfo['sortCount'];
         $justFilterCount = 0;
         $db = eZDB::instance();
         if (is_array($filterArray)) {
             // Handle attribute filters and generate SQL
             $totalAttributesFiltersCount = count($filterArray);
             foreach ($filterArray as $filter) {
                 $isFilterValid = true;
                 // by default assumes that filter is valid
                 $filterAttributeID = $filter[0];
                 $filterType = $filter[1];
                 $filterValue = is_array($filter[2]) ? '' : $db->escapeString($filter[2]);
                 $useAttributeFilter = false;
                 switch ($filterAttributeID) {
                     case 'path':
                         $filterField = 'path_string';
                         break;
                     case 'published':
                         $filterField = 'ezcontentobject.published';
                         break;
                     case 'modified':
                         $filterField = 'ezcontentobject.modified';
                         break;
                     case 'modified_subnode':
                         $filterField = 'modified_subnode';
                         break;
                     case 'node_id':
                         $filterField = 'ezcontentobject_tree.node_id';
                         break;
                     case 'contentobject_id':
                         $filterField = 'ezcontentobject_tree.contentobject_id';
                         break;
                     case 'section':
                         $filterField = 'ezcontentobject.section_id';
                         break;
                     case 'state':
                         // state only supports =, !=, in, and not_in
                         // other operators do not make any sense in this context
                         $hasFilterOperator = true;
                         switch ($filterType) {
                             case '=':
                             case '!=':
                                 $subQueryCondition = 'contentobject_state_id = ' . (int) $filter[2];
                                 $filterOperator = $filterType == '=' ? 'IN' : 'NOT IN';
                                 break;
                             case 'in':
                             case 'not_in':
                                 if (is_array($filter[2])) {
                                     $subQueryCondition = $db->generateSQLINStatement($filter[2], 'contentobject_state_id', false, false, 'int');
                                     $filterOperator = $filterType == 'in' ? 'IN' : 'NOT IN';
                                 } else {
                                     $hasFilterOperator = false;
                                 }
                                 break;
                             default:
                                 $hasFilterOperator = false;
                                 eZDebug::writeError("Unknown attribute filter type for state: {$filterType}", __METHOD__);
                                 break;
                         }
                         if ($hasFilterOperator) {
                             if ($filterCount - $sortingInfo['sortCount'] > 0) {
                                 $attibuteFilterJoinSQL .= " {$filterJoinType} ";
                             }
                             $attibuteFilterJoinSQL .= "ezcontentobject.id {$filterOperator} (SELECT contentobject_id FROM ezcobj_state_link WHERE {$subQueryCondition})";
                             $filterCount++;
                             $justFilterCount++;
                         }
                         continue 2;
                         break;
                     case 'depth':
                         $filterField = 'depth';
                         break;
                     case 'class_identifier':
                         $filterField = 'ezcontentclass.identifier';
                         break;
                     case 'class_name':
                         $classNameFilter = eZContentClassName::sqlFilter();
                         $filterField = $classNameFilter['nameField'];
                         $filterSQL['from'] .= " INNER JOIN {$classNameFilter['from']} ON ({$classNameFilter['where']})";
                         break;
                     case 'priority':
                         $filterField = 'ezcontentobject_tree.priority';
                         break;
                     case 'name':
                         $filterField = 'ezcontentobject_name.name';
                         break;
                     case 'owner':
                         $filterField = 'ezcontentobject.owner_id';
                         break;
                     case 'visibility':
                         $filterValue = $filterValue == '1' ? 0 : 1;
                         $filterField = 'ezcontentobject_tree.is_invisible';
                         break;
                     default:
                         $useAttributeFilter = true;
                         break;
                 }
                 if ($useAttributeFilter) {
                     if (!is_numeric($filterAttributeID)) {
                         $filterAttributeID = eZContentObjectTreeNode::classAttributeIDByIdentifier($filterAttributeID);
                     }
                     if ($filterAttributeID === false) {
                         $isFilterValid = false;
                         if ($filterJoinType === 'AND') {
                             // go out
                             $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
                             break;
                         }
                         ++$invalidAttributesFiltersCount;
                     } else {
                         // Check datatype for filtering
                         $filterDataType = eZContentObjectTreeNode::sortKeyByClassAttributeID($filterAttributeID);
                         if ($filterDataType === false) {
                             $isFilterValid = false;
                             if ($filterJoinType === 'AND') {
                                 // go out
                                 $invalidAttributesFiltersCount = $totalAttributesFiltersCount;
                                 break;
                             }
                             // check next filter
                             ++$invalidAttributesFiltersCount;
                         } else {
                             $sortKey = false;
                             if ($filterDataType == 'string') {
                                 $sortKey = 'sort_key_string';
                             } else {
                                 $sortKey = 'sort_key_int';
                             }
                             $filterField = "a{$filterCount}.{$sortKey}";
                             // Use the same joins as we do when sorting,
                             // if more attributes are filtered by we will append them
                             if ($filterCount >= $sortingInfo['attributeJoinCount']) {
                                 $filterSQL['from'] .= " INNER JOIN ezcontentobject_attribute a{$filterCount} ON (a{$filterCount}.contentobject_id = ezcontentobject.id) ";
                             }
                             // Ref http://issues.ez.no/19190
                             // If language param is set, we must take it into account.
                             if ($language) {
                                 eZContentLanguage::setPrioritizedLanguages($language);
                             }
                             $filterSQL['where'] .= "\n                                  a{$filterCount}.contentobject_id = ezcontentobject.id AND\n                                  a{$filterCount}.contentclassattribute_id = {$filterAttributeID} AND\n                                  a{$filterCount}.version = ezcontentobject_name.content_version AND ";
                             $filterSQL['where'] .= eZContentLanguage::sqlFilter("a{$filterCount}", 'ezcontentobject') . ' AND ';
                             if ($language) {
                                 eZContentLanguage::clearPrioritizedLanguages();
                             }
                         }
                     }
                 }
                 if ($isFilterValid) {
                     $hasFilterOperator = true;
                     // Controls quotes around filter value, some filters do this manually
                     $noQuotes = false;
                     // Controls if $filterValue or $filter[2] is used, $filterValue is already escaped
                     $unEscape = false;
                     switch ($filterType) {
                         case '=':
                             $filterOperator = '=';
                             break;
                         case '!=':
                             $filterOperator = '<>';
                             break;
                         case '>':
                             $filterOperator = '>';
                             break;
                         case '<':
                             $filterOperator = '<';
                             break;
                         case '<=':
                             $filterOperator = '<=';
                             break;
                         case '>=':
                             $filterOperator = '>=';
                             break;
                         case 'like':
                         case 'not_like':
                             $filterOperator = $filterType == 'like' ? 'LIKE' : 'NOT LIKE';
                             // We escape the string ourselves, this MUST be done before wildcard replace
                             $filter[2] = $db->escapeString($filter[2]);
                             $unEscape = true;
                             // Since * is used as wildcard we need to transform the string to
                             // use % as wildcard. The following rules apply:
                             // - % -> \%
                             // - * -> %
                             // - \* -> *
                             // - \\ -> \
                             $filter[2] = preg_replace(array('#%#m', '#(?<!\\\\)\\*#m', '#(?<!\\\\)\\\\\\*#m', '#\\\\\\\\#m'), array('\\%', '%', '*', '\\\\'), $filter[2]);
                             break;
                         case 'in':
                         case 'not_in':
                             $filterOperator = $filterType == 'in' ? 'IN' : 'NOT IN';
                             // Turn off quotes for value, we do this ourselves
                             $noQuotes = true;
                             if (is_array($filter[2])) {
                                 reset($filter[2]);
                                 while (list($key, $value) = each($filter[2])) {
                                     // Non-numerics must be escaped to avoid SQL injection
                                     $filter[2][$key] = is_numeric($value) ? $value : "'" . $db->escapeString($value) . "'";
                                 }
                                 $filterValue = '(' . implode(",", $filter[2]) . ')';
                             } else {
                                 $hasFilterOperator = false;
                             }
                             break;
                         case 'between':
                         case 'not_between':
                             $filterOperator = $filterType == 'between' ? 'BETWEEN' : 'NOT BETWEEN';
                             // Turn off quotes for value, we do this ourselves
                             $noQuotes = true;
                             if (is_array($filter[2])) {
                                 // Check for non-numerics to avoid SQL injection
                                 if (!is_numeric($filter[2][0])) {
                                     $filter[2][0] = "'" . $db->escapeString($filter[2][0]) . "'";
                                 }
                                 if (!is_numeric($filter[2][1])) {
                                     $filter[2][1] = "'" . $db->escapeString($filter[2][1]) . "'";
                                 }
                                 $filterValue = $filter[2][0] . ' AND ' . $filter[2][1];
                             }
                             break;
                         default:
                             $hasFilterOperator = false;
                             eZDebug::writeError("Unknown attribute filter type: {$filterType}", __METHOD__);
                             break;
                     }
                     if ($hasFilterOperator) {
                         if ($filterCount - $sortingInfo['sortCount'] > 0) {
                             $attibuteFilterJoinSQL .= " {$filterJoinType} ";
                         }
                         // If $unEscape is true we get the filter value from the 2nd element instead
                         // which must have been escaped by filter type
                         $filterValue = $unEscape ? $filter[2] : $filterValue;
                         $attibuteFilterJoinSQL .= "{$filterField} {$filterOperator} ";
                         $attibuteFilterJoinSQL .= $noQuotes ? "{$filterValue} " : "'{$filterValue}' ";
                         $filterCount++;
                         $justFilterCount++;
                     }
                 }
             }
             // end of 'foreach ( $filterArray as $filter )'
             if ($totalAttributesFiltersCount == $invalidAttributesFiltersCount) {
                 eZDebug::writeNotice("Attribute filter returned false");
                 $filterSQL = false;
             } else {
                 if ($justFilterCount > 0) {
                     $filterSQL['where'] .= "                            ( " . $attibuteFilterJoinSQL . " ) AND ";
                 }
             }
         }
         // end of 'if ( is_array( $filterArray ) )'
     }
     return $filterSQL;
 }