/**
  * Pre-process the selectors to add Page status checks
  *
  */
 protected function setupStatusChecks(Selectors $selectors, $options = array())
 {
     $maxStatus = null;
     foreach ($selectors as $key => $selector) {
         if ($selector->field == 'status') {
             $value = $selector->value;
             if (!ctype_digit("{$value}")) {
                 // allow use of some predefined labels for Page statuses
                 if ($value == 'hidden') {
                     $selector->value = Page::statusHidden;
                 } else {
                     if ($value == 'unpublished') {
                         $selector->value = Page::statusUnpublished;
                     } else {
                         if ($value == 'locked') {
                             $selector->value = Page::statusLocked;
                         } else {
                             $selector->value = 1;
                         }
                     }
                 }
                 if ($selector->operator == '=') {
                     // there is no point in an equals operator here, so we make it a bitwise AND, for simplification
                     $selectors[$key] = new SelectorBitwiseAnd('status', $selector->value);
                 }
             }
             if (is_null($maxStatus) || $value > $maxStatus) {
                 $maxStatus = (int) $selector->value;
             }
         }
     }
     if (!is_null($maxStatus)) {
         // if a status was already present in the selector, then just make sure the page isn't unpublished
         if ($maxStatus < Page::statusUnpublished) {
             $selectors->add(new SelectorLessThan('status', Page::statusUnpublished));
         }
     } else {
         if ($options['findOne']) {
             // findOne option, apply optimizations enabling hidden pages to be loaded
             $selectors->add(new SelectorLessThan('status', Page::statusUnpublished));
         } else {
             // no status is present, so exclude everything hidden and above
             $selectors->add(new SelectorLessThan('status', Page::statusHidden));
         }
     }
     if ($options['findOne']) {
         $selectors->add(new SelectorEqual('start', 0));
         $selectors->add(new SelectorEqual('limit', 1));
     }
 }
Ejemplo n.º 2
0
 /**
  * Add all individual selector types to the runtime Selectors
  *
  */
 public static function loadSelectorTypes()
 {
     Selectors::addType(SelectorEqual::getOperator(), 'SelectorEqual');
     Selectors::addType(SelectorNotEqual::getOperator(), 'SelectorNotEqual');
     Selectors::addType(SelectorGreaterThan::getOperator(), 'SelectorGreaterThan');
     Selectors::addType(SelectorLessThan::getOperator(), 'SelectorLessThan');
     Selectors::addType(SelectorGreaterThanEqual::getOperator(), 'SelectorGreaterThanEqual');
     Selectors::addType(SelectorLessThanEqual::getOperator(), 'SelectorLessThanEqual');
     Selectors::addType(SelectorContains::getOperator(), 'SelectorContains');
     Selectors::addType(SelectorContainsLike::getOperator(), 'SelectorContainsLike');
     Selectors::addType(SelectorContainsWords::getOperator(), 'SelectorContainsWords');
     Selectors::addType(SelectorStarts::getOperator(), 'SelectorStarts');
     Selectors::addType(SelectorStartsLike::getOperator(), 'SelectorStartsLike');
     Selectors::addType(SelectorEnds::getOperator(), 'SelectorEnds');
     Selectors::addType(SelectorEndsLike::getOperator(), 'SelectorEndsLike');
     Selectors::addType(SelectorBitwiseAnd::getOperator(), 'SelectorBitwiseAnd');
 }
Ejemplo n.º 3
0
 /**
  * Pre-process the selectors to add Page status checks
  *
  */
 protected function setupStatusChecks(Selectors $selectors)
 {
     $maxStatus = null;
     $options = $this->options;
     foreach ($selectors as $key => $selector) {
         if ($selector->field == 'status') {
             $value = $selector->value;
             if (!ctype_digit("{$value}")) {
                 // allow use of some predefined labels for Page statuses
                 if ($value == 'hidden') {
                     $selector->value = Page::statusHidden;
                 } else {
                     if ($value == 'unpublished') {
                         $selector->value = Page::statusUnpublished;
                     } else {
                         if ($value == 'locked') {
                             $selector->value = Page::statusLocked;
                         } else {
                             if ($value == 'max') {
                                 $selector->value = Page::statusMax;
                             } else {
                                 $selector->value = 1;
                             }
                         }
                     }
                 }
                 if ($selector->operator == '=') {
                     // there is no point in an equals operator here, so we make it a bitwise AND, for simplification
                     $selectors[$key] = new SelectorBitwiseAnd('status', $selector->value);
                 }
             }
             if (is_null($maxStatus) || $value > $maxStatus) {
                 $maxStatus = (int) $selector->value;
             }
         } else {
             if ($selector->field == 'include' && $selector->operator == '=' && in_array($selector->value, array('hidden', 'all'))) {
                 if ($selector->value == 'hidden') {
                     $options['findHidden'] = true;
                 } else {
                     if ($selector->value == 'all') {
                         $options['findAll'] = true;
                     }
                 }
                 $selectors->remove($key);
             } else {
                 if ($selector->field == 'check_access' || $selector->field == 'checkAccess') {
                     $this->checkAccess = (int) $selector->value > 0 ? true : false;
                     $selectors->remove($key);
                 }
             }
         }
     }
     if (!is_null($maxStatus)) {
         // if a status was already present in the selector, then just make sure the page isn't unpublished
         if ($maxStatus < Page::statusUnpublished) {
             $selectors->add(new SelectorLessThan('status', Page::statusUnpublished));
         }
     } else {
         if ($options['findAll']) {
             // findAll option means that unpublished, hidden, trash, system may be included
             $selectors->add(new SelectorLessThan('status', Page::statusMax));
             $this->checkAccess = false;
         } else {
             if ($options['findOne'] || $options['findHidden']) {
                 // findHidden|findOne option, apply optimizations enabling hidden pages to be loaded
                 $selectors->add(new SelectorLessThan('status', Page::statusUnpublished));
             } else {
                 // no status is present, so exclude everything hidden and above
                 $selectors->add(new SelectorLessThan('status', Page::statusHidden));
             }
         }
     }
     if ($options['findOne']) {
         $selectors->add(new SelectorEqual('start', 0));
         $selectors->add(new SelectorEqual('limit', 1));
     }
 }
Ejemplo n.º 4
0
 /**
  * Get the value of a requested Page property
  *
  * @param string $key
  * @return mixed
  * @see __get
  *
  */
 public function get($key)
 {
     if (is_array($key)) {
         $key = implode('|', $key);
     }
     $value = null;
     switch ($key) {
         case 'parent_id':
         case 'parentID':
             $value = $this->parent ? $this->parent->id : 0;
             break;
         case 'child':
             $value = $this->child();
             break;
         case 'children':
         case 'subpages':
             // PW1
             $value = $this->children();
             break;
         case 'has_parent':
         case 'hasParent':
             $value = $this->parents();
             break;
         case 'parent':
         case 'parents':
         case 'rootParent':
         case 'siblings':
         case 'next':
         case 'prev':
         case 'url':
         case 'path':
         case 'outputFormatting':
         case 'isTrash':
             $value = $this->{$key}();
             break;
         case 'httpUrl':
         case 'httpURL':
             $value = $this->httpUrl();
             break;
         case 'fieldgroup':
         case 'fields':
             $value = $this->template->fieldgroup;
             break;
         case 'template_id':
         case 'templates_id':
         case 'templateID':
         case 'templatesID':
             $value = $this->template ? $this->template->id : 0;
             break;
         case 'template':
         case 'templatePrevious':
         case 'parentPrevious':
         case 'namePrevious':
         case 'statusPrevious':
         case 'isLoaded':
         case 'isNew':
         case 'pageNum':
         case 'instanceID':
             $value = $this->{$key};
             break;
         case 'out':
         case 'output':
             $value = $this->output();
             break;
         case 'filesManager':
             $value = $this->filesManager();
             break;
         case 'name':
             $value = $this->settings['name'];
             break;
         case 'modified_users_id':
         case 'modifiedUsersID':
         case 'modifiedUserID':
             $value = (int) $this->settings['modified_users_id'];
             break;
         case 'created_users_id':
         case 'createdUsersID':
         case 'createdUserID':
             $value = (int) $this->settings['created_users_id'];
             break;
         case 'modifiedUser':
             if (!$this->modifiedUser) {
                 if ($this->settings['modified_users_id'] == $this->wire('user')->id) {
                     $this->modifiedUser = $this->wire('user');
                 } else {
                     $this->modifiedUser = $this->wire('users')->get((int) $this->settings['modified_users_id']);
                 }
             }
             $this->modifiedUser->of($this->of());
             $value = $this->modifiedUser;
             break;
         case 'createdUser':
             if (!$this->createdUser) {
                 if ($this->settings['created_users_id'] == $this->wire('user')->id) {
                     $this->createdUser = $this->wire('user');
                 } else {
                     $this->createdUser = $this->wire('users')->get((int) $this->settings['created_users_id']);
                 }
             }
             $this->createdUser->of($this->of());
             $value = $this->createdUser;
             break;
         case 'urlSegment':
             $value = $this->wire('input')->urlSegment1;
             // deprecated, but kept for backwards compatibility
             break;
         case 'accessTemplate':
             $value = $this->getAccessTemplate();
             break;
         case 'num_children':
         case 'numChildren':
             $value = $this->settings['numChildren'];
             break;
         case 'numChildrenVisible':
         case 'numVisibleChildren':
         case 'hasChildren':
             $value = $this->numChildren(true);
             break;
         case 'editUrl':
         case 'editURL':
             $value = $this->wire('config')->urls->admin . "page/edit/?id={$this->id}";
             break;
         default:
             if ($key && isset($this->settings[(string) $key])) {
                 return $this->settings[$key];
             }
             if (($value = $this->getFieldFirstValue($key)) !== null) {
                 return $value;
             }
             if (($value = $this->getFieldValue($key)) !== null) {
                 return $value;
             }
             // if there is a selector, we'll assume they are using the get() method to get a child
             if (Selectors::stringHasOperator($key)) {
                 return $this->child($key);
             }
             // check if it's a field.subfield property, but only if output formatting is off
             if (!$this->outputFormatting() && strpos($key, '.') !== false && ($value = $this->getDot($key)) !== null) {
                 return $value;
             }
             // optionally let a hook look at it
             if (self::isHooked('Page::getUnknown()')) {
                 return $this->getUnknown($key);
             }
     }
     return $value;
 }
Ejemplo n.º 5
0
 /**
  * Hook a function/method to a hookable method call in this object
  *
  * Hookable method calls are methods preceded by three underscores. 
  * You may also specify a method that doesn't exist already in the class
  * The hook method that you define may be part of a class or a globally scoped function. 
  * 
  * If you are hooking a procedural function, you may omit the $toObject and instead just call via:
  * $this->addHook($method, 'function_name'); or $this->addHook($method, 'function_name', $options); 
  *
  * @param string $method Method name to hook into, NOT including the three preceding underscores. 
  * 	May also be Class::Method for same result as using the fromClass option.
  * @param object|null|callable $toObject Object to call $toMethod from,
  * 	Or null if $toMethod is a function outside of an object,
  * 	Or function|callable if $toObject is not applicable or function is provided as a closure.
  * @param string $toMethod Method from $toObject, or function name to call on a hook event. Optional.
  * @param array $options See self::$defaultHookOptions at the beginning of this class. Optional.
  * @return string A special Hook ID that should be retained if you need to remove the hook later
  * @throws WireException
  *
  */
 public function addHook($method, $toObject, $toMethod = null, $options = array())
 {
     if (is_array($toMethod)) {
         // $options array specified as 3rd argument
         if (count($options)) {
             // combine $options from addHookBefore/After and user specified options
             $options = array_merge($toMethod, $options);
         } else {
             $options = $toMethod;
         }
         $toMethod = null;
     }
     if (is_null($toMethod)) {
         // $toObject has been ommitted and a procedural function specified instead
         // $toObject may also be a closure
         $toMethod = $toObject;
         $toObject = null;
     }
     if (is_null($toMethod)) {
         throw new WireException("Method to call is required and was not specified (toMethod)");
     }
     if (substr($method, 0, 3) == '___') {
         throw new WireException("You must specify hookable methods without the 3 preceding underscores");
     }
     if (method_exists($this, $method)) {
         throw new WireException("Method " . $this->className() . "::{$method} is not hookable");
     }
     $options = array_merge(self::$defaultHookOptions, $options);
     if (strpos($method, '::')) {
         list($fromClass, $method) = explode('::', $method, 2);
         if (strpos($fromClass, '(') !== false) {
             // extract object selector match string
             list($fromClass, $objMatch) = explode('(', $fromClass, 2);
             $objMatch = trim($objMatch, ') ');
             if (Selectors::stringHasSelector($objMatch)) {
                 $objMatch = new Selectors($objMatch);
             }
             if ($objMatch) {
                 $options['objMatch'] = $objMatch;
             }
         }
         $options['fromClass'] = $fromClass;
     }
     $argOpen = strpos($method, '(');
     if ($argOpen && strpos($method, ')') > $argOpen + 1) {
         // extract argument selector match string(s), arg 0: Something::something(selector_string)
         // or: Something::something(1:selector_string, 3:selector_string) matches arg 1 and 3.
         list($method, $argMatch) = explode('(', $method, 2);
         $argMatch = trim($argMatch, ') ');
         if (strpos($argMatch, ':') !== false) {
             // zero-based argument indexes specified, i.e. 0:template=product, 1:order_status
             $args = preg_split('/\\b([0-9]):/', trim($argMatch), -1, PREG_SPLIT_DELIM_CAPTURE);
             if (count($args)) {
                 $argMatch = array();
                 array_shift($args);
                 // blank
                 while (count($args)) {
                     $argKey = (int) trim(array_shift($args));
                     $argVal = trim(array_shift($args), ', ');
                     $argMatch[$argKey] = $argVal;
                 }
             }
         } else {
             // just single argument specified, so argument 0 is assumed
         }
         if (is_string($argMatch)) {
             $argMatch = array(0 => $argMatch);
         }
         foreach ($argMatch as $argKey => $argVal) {
             if (Selectors::stringHasSelector($argVal)) {
                 $argMatch[$argKey] = new Selectors($argVal);
             }
         }
         if (count($argMatch)) {
             $options['argMatch'] = $argMatch;
         }
     }
     if ($options['allInstances'] || $options['fromClass']) {
         // hook all instances of this class
         $hookClass = $options['fromClass'] ? $options['fromClass'] : $this->className();
         if (!isset(self::$staticHooks[$hookClass])) {
             self::$staticHooks[$hookClass] = array();
         }
         $hooks =& self::$staticHooks[$hookClass];
         $options['allInstances'] = true;
         $local = 0;
     } else {
         // hook only this instance
         $hookClass = '';
         $hooks =& $this->localHooks;
         $local = 1;
     }
     $priority = (string) $options['priority'];
     if (!isset($hooks[$method])) {
         if (ctype_digit($priority)) {
             $priority = "{$priority}.0";
         }
         $hooks[$method] = array();
     } else {
         if (strpos($priority, '.')) {
             // priority already specifies a sub value: extract it
             list($priority, $n) = explode('.', $priority);
             $options['priority'] = $priority;
             // without $n
             $priority .= ".{$n}";
         } else {
             $n = 0;
             $priority .= ".0";
         }
         // come up with a priority that is unique for this class/method across both local and static hooks
         while ($hookClass && isset(self::$staticHooks[$hookClass][$method][$priority]) || isset($this->localHooks[$method][$priority])) {
             $n++;
             $priority = "{$options['priority']}.{$n}";
         }
     }
     // Note hookClass is always blank when this is a local hook
     $id = "{$hookClass}:{$priority}:{$method}";
     $options['priority'] = $priority;
     $hooks[$method][$priority] = array('id' => $id, 'method' => $method, 'toObject' => $toObject, 'toMethod' => $toMethod, 'options' => $options);
     // cacheValue is just the method() or property, cacheKey includes optional fromClass::
     $cacheValue = $options['type'] == 'method' ? "{$method}()" : "{$method}";
     $cacheKey = ($options['fromClass'] ? $options['fromClass'] . '::' : '') . $cacheValue;
     self::$hookMethodCache[$cacheKey] = $cacheValue;
     // keep track of all local hooks combined when debug mode is on
     if ($this->wire('config')->debug && $hooks === $this->localHooks) {
         $debugClass = $this->className();
         $debugID = ($local ? $debugClass : '') . $id;
         while (isset(self::$allLocalHooks[$debugID])) {
             $debugID .= "_";
         }
         $debugHook = $hooks[$method][$priority];
         $debugHook['method'] = $debugClass . "->" . $debugHook['method'];
         self::$allLocalHooks[$debugID] = $debugHook;
     }
     // sort by priority, if more than one hook for the method
     if (count($hooks[$method]) > 1) {
         defined("SORT_NATURAL") ? ksort($hooks[$method], SORT_NATURAL) : uksort($hooks[$method], "strnatcmp");
     }
     return $id;
 }
Ejemplo n.º 6
0
 /**
  * Does this WireArray have the given index or match the given selector?
  *
  * If the WireArray uses numeric keys, then this will also match a wire's "name" field.
  * 
  * @param int|string $key Key of item to check or selector.
  * @return bool True if the item exists, false if not. 
  */
 public function has($key)
 {
     if (is_object($key)) {
         $key = $this->getItemKey($key);
     }
     if (array_key_exists($key, $this->data)) {
         return true;
     }
     if (is_string($key)) {
         if (Selectors::stringHasOperator($key)) {
             $match = $this->findOne($key);
         } else {
             if ($this->usesNumericKeys()) {
                 $match = $this->getItemThatMatches('name', $key);
             }
         }
     } else {
         $match = null;
     }
     return $match ? true : false;
 }
 /**
  * Given a Selectors object or a selector string, return whether this Page matches it
  *
  * @param Page $page
  * @param string|Selectors $s
  * @return bool
  *
  */
 public function matches(Page $page, $s)
 {
     if (is_string($s) || is_int($s)) {
         if (ctype_digit("{$s}")) {
             $s = (int) $s;
         }
         if (is_string($s)) {
             // exit early for simple path comparison
             if (substr($s, 0, 1) == '/' && $page->path() == rtrim($s, '/') . '/') {
                 return true;
             }
             if (!Selectors::stringHasOperator($s)) {
                 return false;
             }
             $selectors = new Selectors($s);
         } else {
             if (is_int($s)) {
                 // exit early for simple ID comparison
                 return $page->id == $s;
             }
         }
     } else {
         if ($s instanceof Selectors) {
             $selectors = $s;
         } else {
             return false;
         }
     }
     $matches = false;
     foreach ($selectors as $selector) {
         $name = $selector->field;
         if (in_array($name, array('limit', 'start', 'sort', 'include'))) {
             continue;
         }
         $matches = true;
         $value = $page->getUnformatted($name);
         if (is_object($value)) {
             // if the current page value resolves to an object
             if ($value instanceof Page) {
                 // if it's a Page, get both the ID and path as allowed comparison values
                 $value = array($value->id, $value->path);
             } else {
                 if ($value instanceof PageArray) {
                     // if it's a PageArray, then get the ID and path of all of them
                     // @todo add support for @ selectors
                     $_value = array();
                     foreach ($value as $v) {
                         $_value[] = $v->id;
                         $_value[] = $v->path;
                     }
                     $value = $_value;
                 } else {
                     if ($value instanceof Template) {
                         $value = array($value->id, $value->name);
                     } else {
                         // otherwise just get the string value of the object
                         $value = "{$value}";
                     }
                 }
             }
         } else {
             if (is_array($value)) {
                 // ok: selector matches will accept an array
             } else {
                 // convert to a string value, whatever it may be
                 $value = "{$value}";
             }
         }
         if (!$selector->matches($value)) {
             $matches = false;
             break;
         }
     }
     return $matches;
 }
 /**
  * Pre-process the given selector to perform any necessary replacements
  *
  * This is primarily used to handle sub-selections, i.e. "bar=foo, id=[this=that, foo=bar]"
  * 
  * @param Selector $selector
  * @return bool Returns false if selector should be skipped over by getQuery()
  *
  */
 protected function preProcessSelector(Selector $selector)
 {
     if ($selector->quote && !is_array($selector->value) && Selectors::stringHasSelector($selector->value)) {
         // selector contains an embedded quoted selector
         if ($selector->quote == '[') {
             // i.e. field=[id>0, name=something, this=that]
             // selector contains another embedded selector that we need to convert to page IDs
             $selectors = new Selectors($selector->value);
             $hasTemplate = false;
             $hasParent = false;
             foreach ($selectors as $s) {
                 if (is_array($s->field)) {
                     continue;
                 }
                 if ($s->field == 'template') {
                     $hasTemplate = true;
                 }
                 if ($s->field == 'parent' || $s->field == 'parent_id') {
                     $hasParent = true;
                 }
             }
             // special handling for page references, detect if parent or template is defined,
             // and add it to the selector if available. This makes it faster.
             if (!$hasTemplate || !$hasParent) {
                 $fields = is_array($selector->field) ? $selector->field : array($selector->field);
                 $templates = array();
                 $parents = array();
                 $findSelector = '';
                 foreach ($fields as $fieldName) {
                     if (strpos($fieldName, '.') !== false) {
                         list($unused, $fieldName) = explode('.', $fieldName);
                     }
                     $field = $this->wire('fields')->get($fieldName);
                     if (!$field) {
                         continue;
                     }
                     if (!$hasTemplate && $field->template_id) {
                         if (is_array($field->template_id)) {
                             $templates = array_merge($templates, $field->template_id);
                         } else {
                             $templates[] = (int) $field->template_id;
                         }
                     }
                     if (!$hasParent && $field->parent_id) {
                         $parents[] = (int) $field->parent_id;
                     }
                     if ($field->findPagesSelector && count($fields) == 1) {
                         $findSelector = $field->findPagesSelector;
                     }
                 }
                 if (count($templates)) {
                     $selectors->prepend(new SelectorEqual('template', $templates));
                 }
                 if (count($parents)) {
                     $selectors->prepend(new SelectorEqual('parent_id', $parents));
                 }
                 if ($findSelector) {
                     foreach (new Selectors($findSelector) as $s) {
                         $selectors->append($s);
                     }
                 }
             }
             $pageFinder = new PageFinder();
             $ids = $pageFinder->findIDs($selectors);
             // populate selector value with array of page IDs
             if (count($ids) == 0) {
                 // subselector resulted in 0 matches
                 // force non-match for this subselector by populating 'id' subfield to field name(s)
                 $fieldNames = array();
                 foreach ($selector->fields as $key => $fieldName) {
                     if (strpos($fieldName, '.') !== false) {
                         // reduce fieldName to just field name without subfield name
                         list($fieldName, $subname) = explode('.', $fieldName);
                         // subname intentionally unused
                     }
                     $fieldName .= '.id';
                     $fieldNames[$key] = $fieldName;
                 }
                 $selector->fields = $fieldNames;
                 $selector->value = 0;
             } else {
                 $selector->value = count($ids) > 1 ? $ids : reset($ids);
             }
             $selector->quote = '';
             /*
             } else {
             	$fieldName = $selector->field ? $selector->field : 'none';
             	if(!isset($this->extraSubSelectors[$fieldName])) $this->extraSubSelectors[$fieldName] = array();
             	$this->extraSubSelectors[$fieldName][] = new Selectors($selector->value);
             	return false;
             }
             */
         } else {
             if ($selector->quote == '(') {
                 // selector contains an quoted selector. At least one () quoted selector must match for each field specified in front of it
                 $fieldName = $selector->field ? $selector->field : 'none';
                 if (!isset($this->extraOrSelectors[$fieldName])) {
                     $this->extraOrSelectors[$fieldName] = array();
                 }
                 $this->extraOrSelectors[$fieldName][] = new Selectors($selector->value);
                 return false;
             }
         }
     }
     return true;
 }
Ejemplo n.º 9
0
 /**
  * Clone an entire page, it's assets and children and return it. 
  *
  * @param Page $page Page that you want to clone
  * @param Page $parent New parent, if different (default=same parent)
  * @param bool $recursive Clone the children too? (default=true)
  * @param array|string $options Optional options that can be passed to clone or save
  * 	- forceID (int): force a specific ID
  * 	- set (array): Array of properties to set to the clone (you can also do this later)
  * 	- recursionLevel (int): recursion level, for internal use only. 
  * @return Page the newly cloned page or a NullPage() with id=0 if unsuccessful.
  * @throws WireException|Exception on fatal error
  *
  */
 public function ___clone(Page $page, Page $parent = null, $recursive = true, $options = array())
 {
     if (is_string($options)) {
         $options = Selectors::keyValueStringToArray($options);
     }
     if (!isset($options['recursionLevel'])) {
         $options['recursionLevel'] = 0;
     }
     // recursion level
     // if parent is not changing, we have to modify name now
     if (is_null($parent)) {
         $parent = $page->parent;
         $n = 1;
         $name = $page->name . '-' . $n;
     } else {
         $name = $page->name;
         $n = 0;
     }
     // make sure that we have a unique name
     while (count($parent->children("name={$name}, include=all"))) {
         $name = $page->name;
         $nStr = "-" . ++$n;
         if (strlen($name) + strlen($nStr) > self::nameMaxLength) {
             $name = substr($name, 0, self::nameMaxLength - strlen($nStr));
         }
         $name .= $nStr;
     }
     // Ensure all data is loaded for the page
     foreach ($page->template->fieldgroup as $field) {
         $page->get($field->name);
     }
     // clone in memory
     $copy = clone $page;
     $copy->id = isset($options['forceID']) ? (int) $options['forceID'] : 0;
     $copy->setIsNew(true);
     $copy->name = $name;
     $copy->parent = $parent;
     // set any properties indicated in options
     if (isset($options['set']) && is_array($options['set'])) {
         foreach ($options['set'] as $key => $value) {
             $copy->set($key, $value);
         }
         if (isset($options['set']['modified'])) {
             $options['quiet'] = true;
             // allow for modified date to be set
             if (!isset($options['set']['modified_users_id'])) {
                 // since 'quiet' also allows modified user to be set, make sure that it
                 // is still updated, if not specifically set.
                 $copy->modified_users_id = $this->wire('user')->id;
             }
         }
         if (isset($options['set']['modified_users_id'])) {
             $options['quiet'] = true;
             // allow for modified user to be set
             if (!isset($options['set']['modified'])) {
                 // since 'quiet' also allows modified tie to be set, make sure that it
                 // is still updated, if not specifically set.
                 $copy->modified = time();
             }
         }
     }
     // tell PW that all the data needs to be saved
     foreach ($copy->template->fieldgroup as $field) {
         $copy->trackChange($field->name);
     }
     $o = $copy->outputFormatting;
     $copy->setOutputFormatting(false);
     $this->cloneReady($page, $copy);
     try {
         $this->cloning = true;
         $options['ignoreFamily'] = true;
         // skip family checks during clone
         $this->save($copy, $options);
     } catch (Exception $e) {
         $this->cloning = false;
         throw $e;
     }
     $this->cloning = false;
     $copy->setOutputFormatting($o);
     // check to make sure the clone has worked so far
     if (!$copy->id || $copy->id == $page->id) {
         return new NullPage();
     }
     // copy $page's files over to new page
     if (PagefilesManager::hasFiles($page)) {
         $copy->filesManager->init($copy);
         $page->filesManager->copyFiles($copy->filesManager->path());
     }
     // if there are children, then recurisvely clone them too
     if ($page->numChildren && $recursive) {
         $start = 0;
         $limit = 200;
         do {
             $children = $page->children("include=all, start={$start}, limit={$limit}");
             $numChildren = $children->count();
             foreach ($children as $child) {
                 /** @var Page $child */
                 $this->clone($child, $copy, true, array('recursionLevel' => $options['recursionLevel'] + 1));
             }
             $start += $limit;
             $this->uncacheAll();
         } while ($numChildren);
     }
     $copy->parentPrevious = null;
     // update pages_parents table, only when at recursionLevel 0 since pagesParents is already recursive
     if ($recursive && $options['recursionLevel'] === 0) {
         $this->saveParents($copy->id, $copy->numChildren);
     }
     $copy->resetTrackChanges();
     $this->cloned($page, $copy);
     $this->debugLog('clone', "page={$page}, parent={$parent}", $copy);
     return $copy;
 }
Ejemplo n.º 10
0
 /**
  * Given a Selectors object or a selector string, return whether this Page matches it
  *
  * @param string|Selectors $s
  * @return bool
  *
  */
 public function matches($s)
 {
     if (is_string($s)) {
         if (!Selectors::stringHasOperator($s)) {
             return false;
         }
         $selectors = new Selectors($s);
     } else {
         if ($s instanceof Selectors) {
             $selectors = $s;
         } else {
             return false;
         }
     }
     $matches = false;
     foreach ($selectors as $selector) {
         $name = $selector->field;
         if (in_array($name, array('limit', 'start', 'sort', 'include'))) {
             continue;
         }
         $matches = true;
         $value = $this->get($name);
         if (!$selector->matches("{$value}")) {
             $matches = false;
             break;
         }
     }
     return $matches;
 }
Ejemplo n.º 11
0
 /**
  * Given a $expire seconds, date, page, or template convert it to an ISO-8601 date
  * 
  * Returns an array of expires info requires multiple parts, like with self::expireSelector.
  * In this case it returns array with array('expires' => date, 'selector' => selector);
  * 
  * @param $expire
  * @return string|array
  * 
  */
 protected function getExpires($expire)
 {
     if (is_object($expire) && $expire->id) {
         if ($expire instanceof Page) {
             // page object
             $expire = $expire->template->id;
         } else {
             if ($expire instanceof Template) {
                 // template object
                 $expire = $expire->id;
             } else {
                 // unknown object, substitute default
                 $expire = time() + self::expireDaily;
             }
         }
     } else {
         if (is_array($expire)) {
             // expire value already prepared by a previous call, just return it
             if (isset($expire['selector']) && isset($expire['expire'])) {
                 return $expire;
             }
         } else {
             if (in_array($expire, array(self::expireNever, self::expireSave))) {
                 // good, we'll take it as-is
                 return $expire;
             } else {
                 if (is_string($expire) && Selectors::stringHasSelector($expire)) {
                     // expire when page matches selector
                     return array('expire' => self::expireSelector, 'selector' => $expire);
                 } else {
                     // account for date format as string
                     if (is_string($expire) && !ctype_digit("{$expire}")) {
                         $expire = strtotime($expire);
                         $isDate = true;
                     } else {
                         $isDate = false;
                     }
                     if ($expire === 0 || $expire === "0") {
                         // zero is allowed if that's what was specified
                         $expire = (int) $expire;
                     } else {
                         // zero is not allowed because it indicates failed type conversion
                         $expire = (int) $expire;
                         if (!$expire) {
                             $expire = self::expireDaily;
                         }
                     }
                     if ($expire > time()) {
                         // a future date has been specified, so we'll keep it
                     } else {
                         if (!$isDate) {
                             // a quantity of seconds has been specified, add it to current time
                             $expire = time() + $expire;
                         }
                     }
                 }
             }
         }
     }
     $expire = date(self::dateFormat, $expire);
     return $expire;
 }
Ejemplo n.º 12
0
 /**
  * Get the value of a requested Page property
  *
  * @param string $key
  * @return mixed
  * @see __get
  *
  */
 public function get($key)
 {
     if (is_array($key)) {
         $key = implode('|', $key);
     }
     $value = null;
     switch ($key) {
         case 'parent_id':
         case 'parentID':
             $value = $this->parent ? $this->parent->id : 0;
             break;
         case 'child':
             $value = $this->child();
             break;
         case 'children':
         case 'subpages':
             // PW1
             $value = $this->children();
             break;
         case 'has_parent':
         case 'hasParent':
             $value = $this->parents();
             break;
         case 'parent':
         case 'parents':
         case 'rootParent':
         case 'siblings':
         case 'next':
         case 'prev':
         case 'url':
         case 'path':
         case 'outputFormatting':
         case 'isTrash':
             $value = $this->{$key}();
             break;
         case 'httpUrl':
         case 'httpURL':
             $value = $this->httpUrl();
             break;
         case 'fieldgroup':
         case 'fields':
             $value = $this->template->fieldgroup;
             break;
         case 'template_id':
         case 'templates_id':
         case 'templateID':
         case 'templatesID':
             $value = $this->template ? $this->template->id : 0;
             break;
         case 'template':
         case 'templatePrevious':
         case 'parentPrevious':
         case 'namePrevious':
         case 'statusPrevious':
         case 'isLoaded':
         case 'isNew':
         case 'pageNum':
         case 'instanceID':
             $value = $this->{$key};
             break;
         case 'out':
         case 'output':
             $value = $this->output();
             break;
         case 'filesManager':
             $value = $this->filesManager();
             break;
         case 'name':
             $value = $this->settings['name'];
             break;
         case 'modified_users_id':
         case 'modifiedUsersID':
         case 'modifiedUserID':
             $value = (int) $this->settings['modified_users_id'];
             break;
         case 'created_users_id':
         case 'createdUsersID':
         case 'createdUserID':
             $value = (int) $this->settings['created_users_id'];
             break;
         case 'modifiedUser':
         case 'createdUser':
             if (!$this->{$key}) {
                 $_key = str_replace('User', '', $key) . '_users_id';
                 $u = $this->wire('user');
                 if ($this->settings[$_key] == $u->id) {
                     $this->set($key, $u);
                     // prevent possible recursion loop
                 } else {
                     $u = $this->wire('users')->get((int) $this->settings[$_key]);
                     $this->set($key, $u);
                 }
             }
             $value = $this->{$key};
             if ($value) {
                 $value->of($this->of());
             }
             break;
         case 'urlSegment':
             $value = $this->wire('input')->urlSegment1;
             // deprecated, but kept for backwards compatibility
             break;
         case 'accessTemplate':
             $value = $this->getAccessTemplate();
             break;
         case 'num_children':
         case 'numChildren':
             $value = $this->settings['numChildren'];
             break;
         case 'numChildrenVisible':
         case 'numVisibleChildren':
         case 'hasChildren':
             $value = $this->numChildren(true);
             break;
         case 'editUrl':
         case 'editURL':
             $value = $this->editUrl();
             break;
         case 'statusStr':
             $value = implode(' ', $this->status(true));
             break;
         case 'modifiedStr':
         case 'createdStr':
         case 'publishedStr':
             $value = $this->settings[str_replace('Str', '', $key)];
             $value = $value ? wireDate($this->wire('config')->dateFormat, $value) : '';
             break;
         default:
             if ($key && isset($this->settings[(string) $key])) {
                 return $this->settings[$key];
             }
             // populate a formatted string with {tag} vars
             if (strpos($key, '{') !== false && strpos($key, '}')) {
                 return $this->getMarkup($key);
             }
             if (($value = $this->getFieldFirstValue($key)) !== null) {
                 return $value;
             }
             if (($value = $this->getFieldValue($key)) !== null) {
                 return $value;
             }
             // if there is a selector, we'll assume they are using the get() method to get a child
             if (Selectors::stringHasOperator($key)) {
                 return $this->child($key);
             }
             // check if it's a field.subfield property
             if (strpos($key, '.') && ($value = $this->getFieldSubfieldValue($key)) !== null) {
                 return $value;
             }
             // optionally let a hook look at it
             if (self::isHooked('Page::getUnknown()')) {
                 $value = $this->getUnknown($key);
             }
     }
     return $value;
 }
Ejemplo n.º 13
0
 /**
  * Filter out Wires that don't match the selector. 
  * 
  * This is applicable to and destructive to the WireArray.
  * This function contains additions and modifications by @niklaka.
  *
  * @param string|Selectors $selectors AttributeSelector string to use as the filter.
  * @param bool $not Make this a "not" filter? (default is false)
  * @return WireArray reference to current [filtered] instance
  *
  */
 protected function filterData($selectors, $not = false)
 {
     if (is_object($selectors) && $selectors instanceof Selectors) {
         // fantastic
     } else {
         if (ctype_digit("{$selectors}")) {
             $selectors = "id={$selectors}";
         }
         $selectors = new Selectors($selectors);
     }
     $sort = array();
     $start = 0;
     $limit = null;
     // leave sort, limit and start away from filtering selectors
     foreach ($selectors as $selector) {
         $remove = true;
         if ($selector->field === 'sort') {
             // use all sort selectors
             $sort[] = $selector->value;
         } else {
             if ($selector->field === 'start') {
                 // use only the last start selector
                 $start = (int) $selector->value;
             } else {
                 if ($selector->field === 'limit') {
                     // use only the last limit selector
                     $limit = (int) $selector->value;
                 } else {
                     // everything else is to be saved for filtering
                     $remove = false;
                 }
             }
         }
         if ($remove) {
             $selectors->remove($selector);
         }
     }
     // now filter the data according to the selectors that remain
     foreach ($this->data as $key => $item) {
         foreach ($selectors as $selector) {
             if (is_array($selector->field)) {
                 $value = array();
                 foreach ($selector->field as $field) {
                     $value[] = (string) $this->getItemPropertyValue($item, $field);
                 }
             } else {
                 $value = (string) $this->getItemPropertyValue($item, $selector->field);
             }
             if ($not === $selector->matches($value)) {
                 unset($this->data[$key]);
             }
         }
     }
     // if $limit has been given, tell sort the amount of rows that will be used
     if (count($sort)) {
         $this->_sort($sort, $limit ? $start + $limit : null);
     }
     if ($start || $limit) {
         $this->data = array_slice($this->data, $start, $limit, true);
     }
     $this->trackChange("filterData:{$selectors}");
     return $this;
 }
Ejemplo n.º 14
0
 /** 
  * Does this page have the specified status number or template name? 
  *
  * See status flag constants at top of Page class
  *
  * @param int|string $status Status number or Template name
  * @return bool
  *
  */
 public function is($status)
 {
     if (is_int($status)) {
         return $this->status & $status;
     } else {
         if (is_string($status) && $this->sanitizer->name($status) == $status) {
             // valid template name
             if ($this->template->name == $status) {
                 return true;
             }
         } else {
             if (Selectors::stringHasOperator($status)) {
                 $matches = false;
                 $selectors = new Selectors($status);
                 foreach ($selectors as $selector) {
                     $matches = true;
                     $value = $this->get($selector->field);
                     if (!$selector->matches("{$value}")) {
                         $matches = false;
                         break;
                     }
                 }
                 return $matches;
             }
         }
     }
     return false;
 }