public function getNodeFilters(NodeQuery $nodeQuery)
 {
     $parameters = $nodeQuery->getParameters();
     $filterParams = array();
     foreach ($parameters as $key => $value) {
         switch ($key) {
             case 'Title.firstChar':
                 $filterParams[$key] = strtolower(substr($value, 0, 1));
                 break;
             case 'ActiveDate.after':
             case 'CreationDate.after':
                 $filterParams[$key] = $this->DateFactory->newLocalDate($value);
                 break;
             case 'ActiveDate.before':
             case 'CreationDate.before':
                 $filterParams[$key] = $this->DateFactory->newLocalDate($value);
                 break;
             case 'ActiveDate.start':
             case 'CreationDate.start':
                 $value = $this->DateFactory->newLocalDate($value);
                 $value->setTime(0, 0, 0);
                 $filterParams[$key] = $value;
                 break;
             case 'ActiveDate.end':
             case 'CreationDate.end':
                 $value = $this->DateFactory->newLocalDate($value);
                 $value->setTime(23, 59, 59);
                 $filterParams[$key] = $value;
                 break;
             case 'Meta.exist':
                 $filterParams[$key] = PartialUtils::unserializeMetaPartials($value);
                 break;
             case 'OutTags.exist':
             case 'InTags.exist':
                 $filterParams[$key] = PartialUtils::unserializeOutPartials($value);
                 break;
             case 'Title.eq':
             case 'Title.ieq':
             case 'Title.like':
             case 'TreeID.childOf':
             case 'TreeID.eq':
             case 'TreeID.depth':
             case 'Status.eq':
             case 'Status.isActive':
             case 'Status.all':
                 $filterParams[$key] = $value;
                 break;
         }
     }
     if (!array_key_exists('Status.eq', $filterParams) && !array_key_exists('Status.isActive', $filterParams) && !array_key_exists('Status.all', $filterParams)) {
         $filterParams['Status.all'] = false;
     }
     return $filterParams;
 }
 public function buildMySQLQuery(DatabaseInterface $db, $tableNodeRef, $table, $tableid, NodeQuery $nodeQuery, $orderObjects, $slugs, $ids = null)
 {
     $table = $db->quoteIdentifier($table);
     $element = $tableNodeRef->getElement();
     $schema = $element->getSchema();
     $q = new Query();
     $q->SELECT('DISTINCT ' . $table . '.' . $tableid . ' as ID', true);
     $q->SELECT($table . '.Slug');
     $mTableC = 1;
     if ($nodeQuery->hasParameter('OrderByInTag')) {
         $inParts = explode(' ', $nodeQuery->getParameter('OrderByInTag'), 2);
         $partials = PartialUtils::unserializeInPartials($inParts[0]);
         foreach ($partials as $partial) {
             if ($partial->getTagElement() && $partial->getTagSlug() && $partial->getTagRole()) {
                 $tTable = $db->quoteIdentifier($this->NodeDBMeta->getInTagsTable($tableNodeRef));
                 $tClause = $this->NodeTagsDAO->getOutTagPartialClause($partial, $db, $tTable);
                 $q->SELECT("OCondTable" . $mTableC . ".SortOrder as OSortOrder{$mTableC}");
                 $q->JOIN("INNER JOIN {$tTable} as OCondTable{$mTableC} " . str_replace($tTable, 'OCondTable' . $mTableC, " ON {$table}.{$tableid} = {$tTable}.{$tableid} AND {$tClause}"));
                 $direction = array_key_exists(1, $inParts) ? $inParts[1] : 'ASC';
                 $q->ORDERBY('OCondTable' . $mTableC . '.SortOrder ' . $direction);
                 $mTableC++;
             } else {
                 throw new NodeException('Invalid OrderByInTag parameter, must be fully qualified TagPartial with element:slug#role');
             }
         }
     }
     if ($nodeQuery->hasParameter('OrderByOutTag')) {
         $outParts = explode(' ', $nodeQuery->getParameter('OrderByOutTag'), 2);
         $partials = PartialUtils::unserializeOutPartials($outParts[0]);
         foreach ($partials as $partial) {
             if ($partial->getTagElement() && $partial->getTagSlug() && $partial->getTagRole()) {
                 $tTable = $db->quoteIdentifier($this->NodeDBMeta->getOutTagsTable($tableNodeRef));
                 $tClause = $this->NodeTagsDAO->getOutTagPartialClause($partial, $db, $tTable);
                 $q->SELECT("OCondTable" . $mTableC . ".SortOrder as OSortOrder{$mTableC}");
                 $q->JOIN("INNER JOIN {$tTable} as OCondTable{$mTableC} " . str_replace($tTable, 'OCondTable' . $mTableC, " ON {$table}.{$tableid} = {$tTable}.{$tableid} AND {$tClause}"));
                 $direction = array_key_exists(1, $outParts) ? $outParts[1] : 'ASC';
                 $q->ORDERBY('OCondTable' . $mTableC . '.SortOrder ' . $direction);
                 $mTableC++;
             } else {
                 throw new NodeException('Invalid OrderByOutTag parameter, must be fully qualified TagPartial with element:slug#role');
             }
         }
     }
     $mTableC = 1;
     foreach ($orderObjects as $orderObject) {
         $column = $orderObject->getColumn();
         $direction = $orderObject->getDirection();
         //if(!in_array($column, array('ID', 'Slug')))
         //{
         if ($orderObject->isMeta()) {
             $partial = $orderObject->getOrderByMetaPartial();
             $datatype = $orderObject->getOrderByMetaDataType();
             $mTable = $this->NodeDBMeta->getMetaTable($tableNodeRef, $datatype);
             $mTableAlias = $db->quoteIdentifier($mTable . $mTableC++);
             $mTable = $db->quoteIdentifier($mTable);
             $mClause = $this->NodeMetaDAO->getMetaPartialClause(new MetaPartial($partial), $datatype, $db, $mTableAlias);
             $q->SELECT($mTableAlias . '.' . $datatype . 'Value as ' . $column);
             $q->JOIN('LEFT JOIN ' . $mTable . ' as ' . $mTableAlias . ' ON ' . $table . '.' . $tableid . ' = ' . $mTableAlias . '.' . $tableid . ' AND ' . $mClause);
             $q->ORDERBY("{$column} {$direction}");
         } else {
             if ($orderObject->isDirectional()) {
                 if (!in_array($orderObject->getColumn(), array('Title', 'Slug', 'SortOrder', 'ActiveDate', 'CreationDate', 'ModifiedDate', 'TreeID'))) {
                     throw new NodeException('Invalid ordering column [' . $orderObject->getColumn() . '], must be Title, Slug, SortOrder, ActiveDate, CreationDate, ModifiedDate, TreeID, or #meta-id');
                 }
                 if ($orderObject->getColumn() != 'Slug') {
                     $q->SELECT($table . '.' . $orderObject->getColumn());
                 }
                 $q->ORDERBY("{$table}.{$column} {$direction}");
             }
         }
         //}
     }
     // Add if clause 2/1/2010 by Craig .. Mysql seems to be doing it's job without this secondary sort.
     if (empty($orderObjects)) {
         $q->ORDERBY('ID DESC');
     }
     $q->FROM($table);
     // ID
     if (!empty($ids)) {
         if (count($ids) > 1) {
             $q->WHERE("{$table}.{$tableid} IN (" . $db->joinQuote($ids) . ")");
         } else {
             if (isset($ids[0])) {
                 $q->WHERE("{$table}.{$tableid} = {$db->quote($ids[0])}");
             }
         }
     }
     // Slug
     if (!empty($slugs)) {
         if (count($slugs) > 1) {
             $q->WHERE("{$table}.Slug IN (" . $db->joinQuote($slugs) . ")");
         } else {
             if (isset($slugs[0])) {
                 $q->WHERE("{$table}.Slug = {$db->quote($slugs[0])}");
             }
         }
     }
     // AlphaIndex
     if (($alpha = $nodeQuery->getParameter('Title.firstChar')) != null) {
         if (strtolower($alpha) == '#') {
             $q->WHERE("(ASCII(LOWER(LEFT({$table}.Title, 1))) < 65) OR (ASCII(LOWER(LEFT({$table}.Title, 1))) BETWEEN 90 AND 97) OR (ASCII(LOWER(LEFT({$table}.Title, 1))) > 122)");
         } else {
             $q->WHERE("LOWER(LEFT({$table}.Title, 1)) = {$db->quote(strtolower($alpha))}");
         }
     }
     // Title
     $this->DTOHelper->buildEqualsFilter($db, $q, $nodeQuery, 'Title.ieq', "{$table}.Title");
     $this->DTOHelper->buildReplaceFilter($db, $q, $nodeQuery, 'Title.eq', "BINARY {$table}.Title = ?");
     // TitleSearch
     $this->DTOHelper->buildReplaceFilter($db, $q, $nodeQuery, 'Title.like', "{$table}.Title LIKE ?", '%#s%');
     // ParentTreeID
     $this->DTOHelper->buildReplaceFilter($db, $q, $nodeQuery, 'TreeID.childOf', "{$table}.TreeID LIKE ?", "#s%");
     // TreeID
     $this->DTOHelper->buildEqualsFilter($db, $q, $nodeQuery, 'TreeID.eq', "{$table}.TreeID");
     // select node by Tree depth (or limit existing selection)
     if (($treeDepth = $nodeQuery->getParameter('TreeID.depth')) !== null) {
         $actualDepth = $treeDepth * 4;
         $q->orWhere("LENGTH({$table}.TreeID) = {$db->quote($actualDepth)}");
     }
     if (($treeid = $nodeQuery->getParameter('TreeID.parentOf')) != null) {
         $depth = strlen($treeid) / 4;
         if ($depth == 1) {
             $q->WHERE('1 = 0');
         } else {
             for ($i = 1; $i < $depth; ++$i) {
                 $ptreeid = substr($treeid, 0, $i * 4);
                 $q->ORWHERE("{$table}.TreeID = {$db->quote($ptreeid)}");
             }
         }
     }
     // ActiveAfter
     // ActiveBefore
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'ActiveDate.after', "{$table}.ActiveDate > ?");
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'ActiveDate.before', "{$table}.ActiveDate <= ?");
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'ActiveDate.start', "{$table}.ActiveDate >= ?", 0, 0, 0);
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'ActiveDate.end', "{$table}.ActiveDate <= ?", 23, 59, 59);
     // CreatedAfter
     // CreatedBefore
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'CreationDate.after', "{$table}.CreationDate > ?");
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'CreationDate.before', "{$table}.CreationDate <= ?");
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'CreationDate.start', "{$table}.CreationDate >= ?", 0, 0, 0);
     $this->DTOHelper->buildDateReplaceFilter($db, $q, $nodeQuery, 'CreationDate.end', "{$table}.CreationDate <= ?", 23, 59, 59);
     // Status
     if (($status = $nodeQuery->getParameter('Status.eq')) != null) {
         switch ($status) {
             case 'published':
                 $q->WHERE("{$table}.Status = 'published'");
                 break;
             case 'draft':
                 $q->WHERE("{$table}.Status = 'draft'");
                 break;
             case 'deleted':
                 $q->WHERE("{$table}.Status = 'deleted'");
                 break;
             default:
                 $q->WHERE("{$table}.Status != 'deleted'");
                 break;
         }
     } else {
         if ($nodeQuery->getParameter('Status.isActive') !== null && StringUtils::strToBool($nodeQuery->getParameter('Status.isActive')) == true) {
             $now = $this->DateFactory->newStorageDate();
             $q->WHERE("{$table}.Status = 'published' AND {$table}.ActiveDate < {$db->quote($now)}");
         } else {
             if ($nodeQuery->getParameter('Status.all') == null || StringUtils::strToBool($nodeQuery->getParameter('Status.all')) == false) {
                 $q->WHERE("{$table}.Status != 'deleted'");
             }
         }
     }
     $metaParams = $this->NodesHelper->getMetaFilters($nodeQuery);
     $tablect = 0;
     foreach ($metaParams as $mArgs) {
         list($full, $name, $operator, $value) = $mArgs;
         $def = $schema->getMetaDef($name);
         $datatype = $def->Datatype;
         $mTable = $db->quoteIdentifier($this->NodeDBMeta->getMetaTable($tableNodeRef, $datatype));
         $clause = ' ' . $db->quoteIdentifier($mTable) . '.Name = ' . $db->quote($name) . " AND ";
         if ($datatype == 'flag') {
             throw new NodeException('Unable to run meta clause on flag datatype');
         }
         if (in_array($datatype, array('text', 'blob', 'mediumtext', 'mediumblob'))) {
             throw new NodeException('Query arguments with #' . $name . ' are not supported');
         }
         switch ($operator) {
             case 'eq':
                 if ($datatype == 'varchar') {
                     $clause .= " BINARY {$db->quoteIdentifier($mTable)}.{$datatype}Value = {$db->quote($value)}";
                 } else {
                     $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value = {$db->quote($value)}";
                 }
                 break;
             case 'ieq':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value = {$db->quote($value)}";
                 break;
             case 'like':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value LIKE " . $db->quote('%' . $value . '%');
                 break;
             case 'before':
                 $d = $this->DateFactory->newLocalDate($value);
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value <= {$db->quote($d)}";
                 break;
             case 'after':
                 $d = $this->DateFactory->newLocalDate($value);
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value > {$db->quote($d)}";
                 break;
             case 'start':
                 $d = $this->DateFactory->newLocalDate($value);
                 $d->setTime(0, 0, 0);
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value >= {$db->quote($d)}";
                 break;
             case 'end':
                 $d = $this->DateFactory->newLocalDate($value);
                 $d->setTime(23, 59, 59);
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value <= {$db->quote($d)}";
                 break;
             case 'notEq':
                 if ($datatype == 'varchar') {
                     $clause .= " BINARY {$db->quoteIdentifier($mTable)}.{$datatype}Value != {$db->quote($value)}";
                 } else {
                     $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value != {$db->quote($value)}";
                 }
                 break;
             case 'lessThan':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value < {$db->quote($value)}";
                 break;
             case 'lessThanEq':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value <= {$db->quote($value)}";
                 break;
             case 'greaterThan':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value > {$db->quote($value)}";
                 break;
             case 'greaterThanEq':
                 $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value >= {$db->quote($value)}";
                 break;
                 /*
                  * case insensitive comparison for #meta.in filtering.
                  */
             /*
              * case insensitive comparison for #meta.in filtering.
              */
             case 'in':
                 $inValues = explode(',', $value);
                 if (count($inValues) > 1) {
                     $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value IN ({$db->joinQuote($inValues)})";
                 } else {
                     if ($datatype == 'varchar') {
                         $clause .= " BINARY {$db->quoteIdentifier($mTable)}.{$datatype}Value = {$db->quote($value)}";
                     } else {
                         $clause .= " {$db->quoteIdentifier($mTable)}.{$datatype}Value = {$db->quote($value)}";
                     }
                 }
                 break;
         }
         $tablect++;
         $q->JOIN("INNER JOIN {$mTable} as CondTable{$tablect} " . str_replace($mTable, 'CondTable' . $tablect, " ON {$table}.{$tableid} = {$mTable}.{$tableid} AND {$clause}"));
     }
     // IncludesMeta
     //        if(($im = $nodeQuery->getParameter('Meta.exist')) != NULL ) {
     //            $metas       = PartialUtils::unserializeMetaPartials($im);
     //            $conditions = array();
     //            foreach($metas as $partial) {
     //                $s = $schema->getMetaDef($partial->getMetaName());
     //
     //                $datatype = $s->Datatype;
     //
     //                $mTable = $db->quoteIdentifier($this->NodeDBMeta->getMetaTable($tableNodeRef, $datatype));
     //                $mClause = $this->NodeMetaDAO->getMetaPartialClause($partial, $datatype, $db, $mTable);
     //
     //                $conditions[] = "{$table}.{$tableid} IN
     //                    (SELECT {$mTable}.{$tableid}
     //                    FROM {$mTable}
     //                    WHERE ".$mClause.")";
     ////                  $conditions[] = "EXISTS (SELECT 1 FROM {$this->model->getMetaTable()}
     ////                      WHERE {$this->model->getMetaTable()}.{$this->model->getTableID()} = {$table}.{$this->model->getTableID()}
     ////                      AND ".$partial->getTagClause($this->model->getMetaTable()).")";
     //            }
     //            if (!empty($conditions)) {
     //                $q->WHERE(join(' OR ', $conditions));
     //            }
     //
     //        }
     // IncludesAllMeta
     if (($iam = $nodeQuery->getParameter('Meta.exist')) != NULL) {
         $metas = PartialUtils::unserializeMetaPartials($iam);
         foreach ($metas as $partial) {
             $s = $schema->getMetaDef($partial->getMetaName());
             $datatype = $s->Datatype;
             $mTable = $db->quoteIdentifier($this->NodeDBMeta->getMetaTable($tableNodeRef, $datatype));
             $mClause = $this->NodeMetaDAO->getMetaPartialClause($partial, $datatype, $db, $mTable);
             $tablect++;
             $q->JOIN("INNER JOIN {$mTable} as CondTable{$tablect} " . str_replace($mTable, 'CondTable' . $tablect, " ON {$table}.{$tableid} = {$mTable}.{$tableid} AND {$mClause}"));
             //                $q->WHERE("{$table}.{$tableid} IN
             //                    (SELECT {$mTable}.{$tableid}
             //                    FROM {$mTable}
             //                    WHERE ".$mClause.")");
             //                  $this->db->WHERE("EXISTS (SELECT 1 FROM {$this->model->getMetaTable()}
             //                      WHERE {$this->model->getMetaTable()}.{$this->model->getTableID()} = {$table}.{$this->model->getTableID()}
             //                      AND ".$partial->getTagClause($this->model->getMetaTable()).")");
         }
     }
     // IncludesOutTags
     //        if(($iot = $nodeQuery->getParameter('IncludesOutTags')) != NULL ) {
     //            $tags       = PartialUtils::unserializeOutPartials($iot);
     //            $conditions = array();
     //            foreach($tags as $partial) {
     //                $tTable = $db->quoteIdentifier($this->NodeDBMeta->getOutTagsTable($tableNodeRef));
     //
     //                // TODO: need to support aspects
     //                // TODO: convert OR clauses into multiple unions
     //
     //                $tClause = $this->NodeTagsDAO->getOutTagPartialClause($partial, $tableNodeRef->getSite(), $db, $tTable);
     //
     //                $conditions[] = "{$table}.{$tableid} IN
     //                    (SELECT {$tTable}.{$tableid}
     //                    FROM {$tTable}
     //                    WHERE {$tClause})";
     ////                  $conditions[] = "EXISTS (SELECT 1 FROM {$this->model->getTagsTable()} t2
     ////                      INNER JOIN tags2 ON t2.tagid = tags2.tagid
     ////                      WHERE {$table}.{$this->model->getTableID()} = t2.{$this->model->getTableID()} AND
     ////                      ".$partial->getTagClause("tags2").")";
     //            }
     //            if (!empty($conditions)) {
     //                $q->WHERE(join(' OR ', $conditions));
     //            }
     //
     //        }
     // IncludesAllOutTags
     if (($iaot = $nodeQuery->getParameter('OutTags.exist')) != NULL) {
         $tags = PartialUtils::unserializeOutPartials($iaot);
         foreach ($tags as $partial) {
             $tTable = $db->quoteIdentifier($this->NodeDBMeta->getOutTagsTable($tableNodeRef));
             $tClause = $this->NodeTagsDAO->getOutTagPartialClause($partial, $db, $tTable);
             $tablect++;
             $q->JOIN("INNER JOIN {$tTable} as CondTable{$tablect} " . str_replace($tTable, 'CondTable' . $tablect, " ON {$table}.{$tableid} = {$tTable}.{$tableid} AND {$tClause}"));
             //                $q->WHERE( "{$table}.{$tableid} IN
             //                    (SELECT {$tTable}.{$tableid}
             //                    FROM {$tTable}
             //                    WHERE {$tClause})" );
             //                  $this->db->WHERE("EXISTS (SELECT 1 FROM {$this->model->getTagsTable()} t2
             //                      INNER JOIN tags2 ON t2.tagid = tags2.tagid
             //                      WHERE {$table}.{$this->model->getTableID()} = t2.{$this->model->getTableID()} AND
             //                      ".$partial->getTagClause("tags2").")");
         }
     }
     // IncludesInTags
     //        if(($iit = $nodeQuery->getParameter('IncludesInTags')) != NULL ) {
     //            $tags       = PartialUtils::unserializeInPartials($iit);
     //            $conditions = array();
     //            foreach($tags as $partial) {
     //                $tTable = $db->quoteIdentifier($this->NodeDBMeta->getInTagsTable($tableNodeRef));
     //                $tClause = $this->NodeTagsDAO->getInTagPartialClause($partial, $tableNodeRef->getElement(), $tableNodeRef->getSite(), $db, $tTable);
     //
     //                $conditions[] = "{$table}.{$tableid} IN
     //                    (SELECT {$tTable}.{$tableid}
     //                    FROM {$tTable}
     //                    WHERE {$tClause})";
     ////                  $conditions[] = "EXISTS (SELECT 1 FROM {$this->model->getTagsTable()} t2
     ////                      INNER JOIN tags2 ON t2.tagid = tags2.tagid
     ////                      WHERE {$table}.{$this->model->getTableID()} = t2.{$this->model->getTableID()} AND
     ////                      ".$partial->getTagClause("tags2").")";
     //            }
     //            if (!empty($conditions)) {
     //                $q->WHERE(join(' OR ', $conditions));
     //            }
     //
     //        }
     // IncludesAllInTags
     if (($iait = $nodeQuery->getParameter('InTags.exist')) != NULL) {
         $tags = PartialUtils::unserializeInPartials($iait);
         foreach ($tags as $partial) {
             $tTable = $db->quoteIdentifier($this->NodeDBMeta->getInTagsTable($tableNodeRef));
             $tClause = $this->NodeTagsDAO->getInTagPartialClause($partial, $tableNodeRef->getElement(), $db, $tTable);
             $tablect++;
             $q->JOIN("INNER JOIN {$tTable} as CondTable{$tablect} " . str_replace($tTable, 'CondTable' . $tablect, " ON {$table}.{$tableid} = {$tTable}.{$tableid} AND {$tClause}"));
             //
             //                $q->WHERE( "{$table}.{$tableid} IN
             //                    (SELECT {$tTable}.{$tableid}
             //                    FROM {$tTable}
             //                    WHERE {$tClause})" );
             //                  $this->db->WHERE("EXISTS (SELECT 1 FROM {$this->model->getTagsTable()} t2
             //                      INNER JOIN tags2 ON t2.tagid = tags2.tagid
             //                      WHERE {$table}.{$this->model->getTableID()} = t2.{$this->model->getTableID()} AND
             //                      ".$partial->getTagClause("tags2").")");
         }
     }
     return $q;
 }
 protected function validateMetaPartials($partials_string, $tags, NodeSchema $schema)
 {
     if (empty($partials_string)) {
         if (count($tags) > 0) {
             $this->getErrors()->reject("Meta specified, even though none are in scope.");
         }
     } else {
         $partials = PartialUtils::unserializeMetaPartials($partials_string);
         foreach ($tags as $tag) {
             if (!$this->metaInScope($schema, $tag, $partials)) {
                 $this->getErrors()->reject("Meta: " . $tag->toString() . ' is out of scope.');
             }
         }
     }
 }
 public function saveMeta(DatabaseInterface $db, NodeRef $originNodeRef, $recordid, $metaPartials = 'fields', array $metaTags, $restrictedPartials = '')
 {
     if (empty($recordid)) {
         throw new Exception('Cannot save meta without recordid');
     }
     $originalRestrictedPartials = $restrictedPartials;
     $restrictedPartials = PartialUtils::unserializeMetaPartials($restrictedPartials);
     if ($restrictedPartials == 'all' || ($x = array_search('all', $restrictedPartials)) !== false) {
         return;
     }
     MetaUtils::validateMeta($metaTags);
     //        $originNodeRef = $node->getNodeRef();
     $tableid = $this->NodeDBMeta->getPrimaryKey($originNodeRef);
     $now = $this->DateFactory->newStorageDate();
     $tagsToDelete = array();
     $updatedMeta = array();
     $originalMeta = array();
     //        if($sectionType != null)
     //        {
     //            $schema = $originNodeRef->getElement()->getSchema()->getSectionDef($sectionType);
     //        } else
     //        {
     $schema = $originNodeRef->getElement()->getSchema();
     //        }
     // remove duplicates from meta
     foreach ($metaTags as $o => &$mtag) {
         //            if($sectionid != 0)
         //                $mtag->setMetaSectionID($sectionid);
         $metaDef = $schema->getMetaDef($mtag->getMetaName());
         $mtag->setMetaStorageDatatype($metaDef->Datatype);
         $mtag->setMetaValidationDatatype($metaDef->Validation->getDatatype());
         foreach ($metaTags as $i => $ddtag) {
             //if($o != $i && $tag->getMetaName() === $dtag->getMetaName() && $tag->getMetaSectionID() == $dtag->getMetaSectionID())
             if ($o != $i && $mtag->getMetaName() === $ddtag->getMetaName()) {
                 //                  error_log("REMOVING DUPE ".$dtag->toString());
                 unset($metaTags[$o]);
             }
         }
         foreach ($restrictedPartials as $dPartial) {
             if (strcmp($dPartial->getMetaName(), $mtag['MetaName']) === 0) {
                 unset($metaTags[$o]);
             }
         }
     }
     $currentMetaTags = $this->findMeta($db, $originNodeRef, $recordid, $metaPartials, true, $originalRestrictedPartials);
     //        $this->Logger->debug('current meta for ['.$recordid.'] on section ['.$sectionid.']');
     //        $this->Logger->debug($currentMetaTags);
     MetaUtils::validateMeta($currentMetaTags);
     foreach ($currentMetaTags as $k => $tag) {
         //if($tag->getMetaID() == false) throw new Exception('Cannot save meta without MetaIDs for current meta');
         $foundThisTag = false;
         foreach ($metaTags as $dtag) {
             //if($tag->getMetaName() === $dtag->getMetaName() && $tag->getMetaSectionID() == $dtag->getMetaSectionID())
             if ($tag->getMetaName() === $dtag->getMetaName()) {
                 //                  error_log("MATCHED ".$dtag->toString() ." TO ".$tag->toString());
                 //                  error_log(print_r($dtag, true));
                 //                  error_log(print_r($tag, true));
                 // delete string meta that have no value
                 if (($dtag->getMetaStorageDatatype() == 'text' || $dtag->getMetaStorageDatatype() == 'varchar' || $dtag->getMetaStorageDatatype() == 'blob') && strlen(trim($dtag->getMetaValue())) == 0 || $dtag->getMetaStorageDatatype() == 'flag' && $dtag->getMetaValue() == false || ($dtag->getMetaValidationDatatype() == 'int' || $dtag->getMetaValidationDatatype() == 'float') && $dtag->getMetaValue() === null || in_array($dtag, $updatedMeta)) {
                     //                            error_log('DELETE: '.$tag);
                     $tagsToDelete[] = $tag;
                 } else {
                     //                        error_log('UPDATE: '.$tag);
                     $updatedMeta[$tag->getMetaStorageDatatype()][] = $dtag;
                     $originalMeta[$tag->getMetaStorageDatatype()][] = $tag;
                 }
                 $foundThisTag = true;
                 break;
             }
         }
         if (!$foundThisTag) {
             $tagsToDelete[] = $tag;
         }
     }
     $changedOne = false;
     $tagIDsToDelete = array();
     foreach ($tagsToDelete as $tag) {
         // log tag deletion
         //            $this->transactionsService->logMetaDelete($element, $recordid, $tag);
         $table = $this->NodeDBMeta->getMetaTable($originNodeRef, $tag->getMetaStorageDatatype());
         $affectedRows = $db->deleteRecord($table, "{$tableid} = {$db->quote($recordid)} AND Name = {$db->quote($tag->getMetaName())}");
         if ($affectedRows > 0) {
             $this->NodeEvents->fireMetaEvents('meta', 'remove', $originNodeRef, $tag);
             $changedOne = true;
         }
         //            $tagIDsToDelete[][] = $tag->getMetaID();
     }
     //        if(!empty($tagIDsToDelete)){
     //            foreach($tagIDsToDelete as $datatype => $ids){
     //
     //                ;
     //                $deletions = $db->write("DELETE FROM {$db->quoteIdentifier($table)} WHERE MetaID IN (".$db->joinQuote((array)$ids).")", DatabaseInterface::AFFECTED_ROWS);
     //
     //            }
     //        }
     foreach ($metaTags as $tag) {
         $datatype = $tag->getMetaStorageDatatype();
         $table = $db->quoteIdentifier($this->NodeDBMeta->getMetaTable($originNodeRef, $datatype));
         if (($datatype == 'text' || $datatype == 'varchar' || $datatype == 'blob') && strlen(trim($tag->getMetaValue())) == 0) {
             continue;
         }
         if ($datatype == 'flag' && $tag->getMetaValue() == false) {
             continue;
         }
         if (($tag->getMetaValidationDatatype() == 'int' || $tag->getMetaValidationDatatype() == 'float') && $tag->getMetaValue() === null) {
             continue;
         }
         $datatypeCol = $this->NodeDBMeta->getMetaDatatypeColumn($datatype);
         if (isset($updatedMeta[$datatype]) && in_array($tag, $updatedMeta[$datatype])) {
             $tagid = array_search($tag, $updatedMeta[$datatype]);
             $originalTag = $originalMeta[$datatype][$tagid];
             // if this is an integer storage field and the originalvalue is 0 and the new value equates to 0, do no update
             if (in_array(strtolower($datatypeCol), array('tiny', 'int', 'long', 'float')) && floatVal($originalTag->getMetaValue()) == 0 && floatVal($tag->getMetaValue()) == 0) {
                 continue;
             }
             if ($datatype != 'flag' && strcmp('' . $originalTag->getMetaValue(), '' . $tag->getMetaValue()) !== 0) {
                 // update the meta tag
                 $affectedRows = $db->updateRecord($table, array("{$datatypeCol}Value" => $tag->getMetaValue()), "{$tableid} = {$db->quote($recordid)} AND Name = {$db->quote($tag->getMetaName())}");
                 if ($affectedRows > 0) {
                     $this->NodeEvents->fireMetaEvents('meta', 'update', $originNodeRef, $tag, $originalTag);
                     //                        if($tag->getMetaValidationDatatype() == 'boolean')
                     //                            if($tag->getMetaValue() == false)
                     //                                $this->NodeEvents->fireMetaEvents('meta', 'remove', $originNodeRef, $tag, $originalTag);
                     //                            else
                     //                                $this->NodeEvents->fireMetaEvents('meta', 'add', $originNodeRef, $tag, $originalTag);
                     $changedOne = true;
                 }
             }
         } else {
             // insert the meta tag
             //                $newinsert = array(
             //                    $tableid => $recordid,
             //                    'Name' => $tag->getMetaName(),
             //                    "{$datatype}Value" => $tag->getMetaValue(),
             //                );
             try {
                 if ($datatype == 'flag') {
                     $affectedRows = $db->write("INSERT IGNORE INTO {$table} ({$tableid}, Name) Values ({$db->quote($recordid)}, {$db->quote($tag->getMetaName())})", DatabaseInterface::AFFECTED_ROWS);
                 } else {
                     $affectedRows = $db->write("INSERT INTO {$table} ({$tableid}, Name, {$datatypeCol}Value) Values ({$db->quote($recordid)}, {$db->quote($tag->getMetaName())}, {$db->quote($tag->getMetaValue())})", DatabaseInterface::AFFECTED_ROWS);
                 }
                 //                $bulkTagInserts[$datatype][] = $newinsert;
                 //                $db->insertRecord($table, $newinsert);
                 if ($affectedRows > 0) {
                     $this->NodeEvents->fireMetaEvents('meta', 'add', $originNodeRef, $tag);
                     $changedOne = true;
                 }
             } catch (SQLDuplicateKeyException $dke) {
                 $where = "{$tableid} = {$db->quote($recordid)} AND Name = {$db->quote($tag->getMetaName())}";
                 $originalValue = $db->readField("SELECT {$datatypeCol}Value FROM {$table} WHERE {$where} LOCK IN SHARE MODE");
                 // update the meta tag
                 $affectedRows = $db->updateRecord($table, array("{$datatypeCol}Value" => $tag->getMetaValue()), $where);
                 if ($affectedRows > 0) {
                     $this->NodeEvents->fireMetaEvents('meta', 'update', $originNodeRef, $tag, new Meta($tag->getMetaName(), $originalValue));
                     $changedOne = true;
                 }
             }
             //                $this->NodeEvents->fireMetaEvents('meta', 'add', $originNodeRef, $tag);
         }
     }
     //        if(!empty($bulkTagInserts))
     //            foreach($bulkTagInserts as $datatype => $bulkInserts)
     //                $db->bulkInsertRecords($db->quoteIdentifier($this->NodeDBMeta->getMetaTable($originNodeRef, $datatype)), $bulkInserts);
     return $changedOne;
 }