/**
  * Automatically extracts and replaces the category, keywords and entities
  * for a data object.
  *
  * @param DataObject $object
  */
 public function alchemise(DataObject $object)
 {
     if (!$object->hasExtension('Alchemisable')) {
         throw new Exception('The object must have the Alchemisable extension.');
     }
     $text = $object->getContentForAlchemy();
     if (strlen($text) < $this->charLimit) {
         return;
     }
     $alchemyInfo = $object->AlchemyMetadata->getValues();
     if (!$alchemyInfo) {
         $alchemyInfo = array();
     }
     $cat = $this->getCategoryFor($text);
     $keywords = $this->getKeywordsFor($text);
     $entities = $this->getEntitiesFor($text);
     $alchemyInfo['Category'] = $cat;
     $alchemyInfo['Keywords'] = $keywords;
     $alchemyInfo['Entities'] = $entities;
     $object->AlchemyMetadata = $alchemyInfo;
     //		foreach (Alchemisable::entity_fields() as $field => $name) {
     //			$name = substr($field, 3);
     //
     //			if (array_key_exists($name, $entities)) {
     //				$object->$field = $entities[$name];
     //			} else {
     //				$object->$field = array();
     //			}
     //		}
 }
 /**
  * Gets the workflow definition for a given dataobject, if there is one
  *
  * Will recursively query parent elements until it finds one, if available
  *
  * @param DataObject $dataObject
  */
 public function getDefinitionFor(DataObject $dataObject)
 {
     if ($dataObject->hasExtension('WorkflowApplicable') || $dataObject->hasExtension('FileWorkflowApplicable')) {
         if ($dataObject->WorkflowDefinitionID) {
             return DataObject::get_by_id('WorkflowDefinition', $dataObject->WorkflowDefinitionID);
         }
         if ($dataObject->ParentID) {
             return $this->getDefinitionFor($dataObject->Parent());
         }
         if ($dataObject->hasMethod('workflowParent')) {
             $obj = $dataObject->workflowParent();
             if ($obj) {
                 return $this->getDefinitionFor($obj);
             }
         }
     }
     return null;
 }
 /**
  * @param DataObject $record
  * @param int $index
  */
 public function deleteRecord($record, $index)
 {
     if ($record->hasExtension('Versioned')) {
         $record->deleteFromStage('Stage');
         $record->deleteFromStage('Live');
     } else {
         $record->delete();
     }
 }
 /**
  * Return true or false as to whether a given user can access an object
  * 
  * @param DataObject $node
  *			The object to check perms on
  * @param string $perm
  *			The permission to check against
  * @param Member $member 
  *			The member to check - if not set, the current user is used
  * 
  * @return type 
  */
 public function checkPerm(DataObject $node, $perm, $member = null)
 {
     // if the node doesn't use the extension, fall back to SS logic
     if (!$node->hasExtension('Restrictable')) {
         switch ($perm) {
             case 'View':
                 return $node->canView($member);
             case 'Write':
                 return $node->canEdit($member);
             default:
                 return $node->can($perm, $member);
         }
     }
     if (!$node) {
         return false;
     }
     if (!$member) {
         $member = singleton('SecurityContext')->getMember();
     }
     if (is_int($member)) {
         $member = DataObject::get_by_id('Member', $member);
     }
     if (Permission::check('ADMIN', 'any', $member)) {
         return true;
     }
     $permCache = $this->getCache();
     /* @var $permCache Zend_Cache_Core */
     $key = $this->permCacheKey($node, $perm);
     $userGrants = null;
     if ($key) {
         $userGrants = $permCache->load($key);
         if (count($userGrants)) {
             $userGrants = $this->sanitiseCacheData($userGrants);
         }
     }
     if ($member && $userGrants && isset($userGrants[$perm][$member->ID])) {
         return $userGrants[$perm][$member->ID];
     }
     // okay, we need to build up all the info we have about the node for permissions
     $s = $this->realiseAllSources($node);
     if (!$userGrants) {
         $userGrants = array();
     }
     if (!isset($userGrants[$perm])) {
         $userGrants[$perm] = array();
     }
     $result = null;
     // if no member, just check public view
     $public = $this->checkPublicPerms($node, $perm);
     if ($public) {
         $result = true;
     }
     // can return immediately
     if (!$member) {
         return $result;
     }
     if (is_null($result)) {
         // see whether we're the owner, and if the perm we're checking is in that list
         if ($this->checkOwnerPerms($node, $perm, $member)) {
             $result = true;
         }
     }
     $accessAuthority = '';
     $directGrant = null;
     $can = false;
     if (is_null($result)) {
         $filter = array('ItemID' => $node->ID, 'ItemType' => $node->class);
         $existing = DataList::create('AccessAuthority')->filter($filter);
         // get all access authorities for this object
         $gids = isset($this->groups[$member->ID]) ? $this->groups[$member->ID] : null;
         if (!$gids) {
             $groups = $member ? $member->Groups() : array();
             $gids = array();
             if ($groups && $groups->Count()) {
                 $gids = $groups->map('ID', 'ID')->toArray();
             }
             $this->groups[$member->ID] = $gids;
         }
         $can = false;
         $directGrant = 'NONE';
         if ($existing && $existing->count()) {
             foreach ($existing as $access) {
                 // check if this mentions the perm in question
                 $perms = $access->Perms->getValues();
                 if ($perms) {
                     if (!in_array($perm, $perms)) {
                         continue;
                     }
                 }
                 $grant = null;
                 $authority = $access->getAuthority();
                 if ($authority instanceof Group) {
                     if (isset($gids[$access->AuthorityID])) {
                         $grant = $access->Grant;
                     }
                 } elseif ($authority instanceof Member) {
                     if ($member->ID == $access->AuthorityID) {
                         $grant = $access->Grant;
                     }
                 } else {
                     // another mechanism that will require a lookup of members in a list
                     // TODO cache this
                     if ($authority instanceof ListOfMembers) {
                         $listMembers = $authority->getAllMembers()->map('ID', 'Title');
                         if (isset($listMembers[$member->ID])) {
                             $grant = $access->Grant;
                         }
                     }
                 }
                 if ($grant) {
                     // if it's deny, we can just break away immediately, otherwise we need to evaluate all the
                     // others in case there's another DENY in there somewhere
                     if ($grant === 'DENY') {
                         $directGrant = 'DENY';
                         // immediately break
                         break;
                     } else {
                         // mark that it's been granted for now
                         $directGrant = 'GRANT';
                     }
                 }
             }
         }
     }
     // return immediately if we have something
     if ($directGrant === 'GRANT') {
         $result = true;
     }
     if ($directGrant === 'DENY') {
         $result = false;
     }
     // otherwise query our parents
     if (is_null($result) && $node->InheritPerms) {
         $permParents = $this->getEffectiveParents($node);
         if (count($permParents) || $permParents instanceof IteratorAggregate) {
             foreach ($permParents as $permParent) {
                 if ($permParent && $this->checkPerm($permParent, $perm, $member)) {
                     $result = true;
                 }
             }
         }
     }
     if (is_null($result)) {
         $result = false;
     }
     $userGrants[$perm][$member->ID] = $result;
     if ($key) {
         $permCache->save($userGrants, $key);
     }
     return $result;
 }
 /**
  * @param DataObject $rec           This would normally just be a singleton but we don't want to have to create it over and over
  * @param string     $filterField
  * @param mixed      $filterVal
  * @return array - returns the new filter added
  */
 public function processFilterField($rec, $filterField, $filterVal)
 {
     // First check for VFI fields
     if ($rec->hasExtension('VirtualFieldIndex') && ($spec = $rec->getVFISpec($filterField))) {
         if ($spec['Type'] == VirtualFieldIndex::TYPE_LIST) {
             // Lists have to be handled a little differently
             $f = $rec->getVFIFieldName($filterField) . ':PartialMatch';
             if (is_array($filterVal)) {
                 foreach ($filterVal as &$val) {
                     $val = '|' . $val . '|';
                 }
                 return array($f => $filterVal);
             } else {
                 return array($f => '|' . $filterVal . '|');
             }
         } else {
             // Simples are simple
             $filterField = $rec->getVFIFieldName($filterField);
         }
     }
     // Next check for regular db fields
     if ($rec->dbObject($filterField)) {
         // Is it a range value?
         if (preg_match('/^RANGE\\~(.+)\\~(.+)$/', $filterVal, $m)) {
             $filterField .= ':Between';
             $filterVal = array_slice($m, 1, 2);
         }
         return array($filterField => $filterVal);
     }
     return array();
 }
 /**
  * Start a workflow based on a particular definition for a particular object.
  *
  * The object is optional; if not specified, it is assumed that this workflow
  * is simply a task based checklist type of workflow.
  *
  * @param WorkflowDefinition $definition
  * @param DataObject $for
  */
 public function beginWorkflow(WorkflowDefinition $definition, DataObject $for = null)
 {
     if (!$this->ID) {
         $this->write();
     }
     if ($for && ($for->hasExtension('WorkflowApplicable') || $for->hasExtension('FileWorkflowApplicable'))) {
         $this->TargetClass = ClassInfo::baseDataClass($for);
         $this->TargetID = $for->ID;
     }
     // lets create the first WorkflowActionInstance.
     $action = $definition->getInitialAction()->getInstanceForWorkflow();
     $action->WorkflowID = $this->ID;
     $action->write();
     $title = $for && $for->hasField('Title') ? sprintf(_t('WorkflowInstance.TITLE_FOR_DO', '%s - %s'), $definition->Title, $for->Title) : sprintf(_t('WorkflowInstance.TITLE_STUB', 'Instance #%s of %s'), $this->ID, $definition->Title);
     $this->Title = $title;
     $this->DefinitionID = $definition->ID;
     $this->CurrentActionID = $action->ID;
     $this->InitiatorID = Member::currentUserID();
     $this->write();
     $this->Users()->addMany($definition->Users());
     $this->Groups()->addMany($definition->Groups());
 }
	/**
	 * Get the available stages for the given DataObject.
	 * 
	 * @param DataObject $dataObject
	 * 
	 * @return array
	 */
	public function getDataObjectStages(DataObject $dataObject) {
		return $dataObject->hasExtension('Versioned')?
			$dataObject->getStages():
			array('Stage');
	}
 /**
  * Extracts tags from an object's content where the tag is preceded by a #
  * 
  * @param MicroPost $object 
  * 
  */
 public function extractTags(DataObject $object, $field = 'Content')
 {
     if (!$object->hasExtension('TaggableExtension')) {
         return array();
     }
     $content = $object->{$field};
     if (preg_match_all('/#([a-z0-9_-]+)/is', $content, $matches)) {
         $object->tag($matches[1], true);
     }
     return $object->Tags();
 }
 /**
  * Counts as "archived" if the current record is a different version from both live and draft.
  * 
  * @return boolean
  */
 public function isArchived()
 {
     if (!$this->record->hasExtension('Versioned')) {
         return false;
     }
     if (!isset($this->record->_cached_isArchived)) {
         $baseTable = ClassInfo::baseDataClass($this->record->class);
         $currentDraft = Versioned::get_one_by_stage($baseTable, 'Stage', array("\"{$baseTable}\".\"ID\"" => $this->record->ID));
         $currentLive = Versioned::get_one_by_stage($baseTable, 'Live', array("\"{$baseTable}\".\"ID\"" => $this->record->ID));
         $this->record->_cached_isArchived = (!$currentDraft || $currentDraft && $this->record->Version != $currentDraft->Version) && (!$currentLive || $currentLive && $this->record->Version != $currentLive->Version);
     }
     return $this->record->_cached_isArchived;
 }
 /**
  * this method is a bit of a hack.
  * if a product variation does not have any specific tax rules
  * but the product does, then it uses the rules from the product.
  * @param DataObject $buyable
  */
 function dealWithProductVariationException($buyable)
 {
     if ($buyable instanceof ProductVariation) {
         if (!$buyable->hasExtension("GSTTaxDecorator")) {
             if ($parent = $buyable->Parent()) {
                 if ($parent->hasExtension("GSTTaxDecorator")) {
                     $buyable = $parent;
                 }
             }
         }
     }
 }