/**
  * This method associates a flowing item with certain workflow and creates
  * an active 'flow' for it, unless there's already another active 'flow'
  * for such combination.
  *
  * @param \TooBasic\Workflows\Item $item Flowing item to be injected.
  * @param string $workflowName Name of the workflow to associate it with.
  * @param string $error Returns an error message when something went
  * wrong.
  * @return boolean Returns TRUE if the items was injectected successfully.
  */
 public function inject(Item $item, $workflowName, &$error = false)
 {
     //
     // Default values.
     $out = true;
     //
     // Logging operation.
     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Injecting into workflow '{$workflowName}'. Item: " . json_encode($item->toArray()));
     //
     // Loading and checking workflow.
     $workflow = $this->getWorkflow($workflowName);
     if ($workflow) {
         //
         // Basic query prefixes.
         $prefixes = array(GC_DBQUERY_PREFIX_TABLE => $this->_dbprefix, GC_DBQUERY_PREFIX_COLUMN => 'ifl_');
         //
         // Creating a query to search for active flows.
         $query = $this->_db->queryAdapter()->select('wkfl_item_flows', array('workflow' => $workflowName, 'type' => $item->type(), 'item' => $item->id(), 'status' => 'tmp'), $prefixes);
         $stmt = $this->_db->prepare($query[GC_AFIELD_QUERY]);
         //
         // Checking active flows.
         $injected = false;
         foreach (array(WKFL_ITEM_FLOW_STATUS_OK, WKFL_ITEM_FLOW_STATUS_WAIT) as $status) {
             $query[GC_AFIELD_PARAMS]['status'] = $status;
             $stmt->execute($query[GC_AFIELD_PARAMS]);
             if ($stmt->rowCount() > 0) {
                 $injected = true;
                 break;
             }
         }
         //
         // Checking if the new flow can be injected.
         if ($injected) {
             $error = 'Item already injected and running';
             $out = false;
         } else {
             //
             // Creating a query to inject a flow.
             $query = $this->_db->queryAdapter()->insert('wkfl_item_flows', array('workflow' => $workflowName, 'type' => $item->type(), 'item' => $item->id()), $prefixes);
             $stmt = $this->_db->prepare($query[GC_AFIELD_QUERY]);
             $out = boolval($stmt->execute($query[GC_AFIELD_PARAMS]));
         }
     } else {
         $out = false;
         $error = "Unknown workflow '{$workflowName}'";
     }
     //
     // Logging errors if any.
     if (!$out) {
         $this->_log->log(LGGR_LOG_LEVEL_ERROR, $error);
     }
     return $out;
 }
 /**
  * This method is the one in charge of executing a step of a specific
  * item, analyze results and set the next step or errors.
  *
  * @param \TooBasic\Workflows\ItemFlowRepresentation $flow Item flow to be
  * executed.
  * @param \TooBasic\Workflows\Item $item Item to be analyzed
  * @return boolean Returns TRUE the current step pointed the execution of
  * a next step and the flow is not in a stopping status.
  * @throws \TooBasic\Workflows\WorkflowsException
  */
 public function runFor(ItemFlowRepresentation $flow, Item $item)
 {
     //
     // Default values.
     $continue = true;
     $logParams = array('workflow' => $flow->workflow);
     //
     // Loading all required settings.
     $this->load();
     //
     // Global dependencies.
     global $WKFLDefaults;
     //
     // Guessing the current step name.
     $currentStep = $flow->step;
     if (!$currentStep) {
         //
         // Getting the fist step from workflow when there's none.
         $currentStep = $this->_config->startsAt;
         $flow->step = $currentStep;
         $flow->persist();
     }
     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Current step: '{$currentStep}'", $logParams);
     //
     // Shortcuts.
     $stepConfig = $this->_config->steps->{$currentStep};
     $stepPath = Paths::Instance()->customPaths($WKFLDefaults[WKFL_DEFAULTS_PATHS][WKFL_DEFAULTS_PATH_STEPS], Names::SnakeFilename($stepConfig->manager), Paths::ExtensionPHP);
     $stepClass = Names::ClassNameWithSuffix($stepConfig->manager, WKFL_CLASS_SUFFIX_STEP);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, "        path: '{$stepPath}'", $logParams);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, "       class: '{$stepClass}'", $logParams);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, '      config: ' . json_encode($stepConfig), $logParams);
     //
     // Checking if the step's class is already loaded.
     if (!class_exists($stepClass)) {
         //
         // Checking class code file.
         if (!$stepPath || !is_readable($stepPath)) {
             $msg = "Unable to access class for step '{$currentStep}'.";
             $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams);
             throw new WorkflowsException($msg);
         }
         require_once $stepPath;
     }
     //
     // Checking class existence.
     if (!class_exists($stepClass)) {
         $msg = "Class '{$stepClass}' not defined.";
         $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams);
         throw new WorkflowsException($msg);
     }
     //
     // Getting a step execution class.
     $step = new $stepClass($item);
     //
     // Sharing log pointer.
     $step->setLog($this->_log);
     //
     // Step execution
     $step->execute();
     //
     // Reloading item to avoid outdated data.
     $item->reload();
     //
     // Checking that thers a connection configuration for the current
     // item status.
     $connection = false;
     if (isset($stepConfig->connections->{$item->status()})) {
         $connection = $stepConfig->connections->{$item->status()};
     } else {
         $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Unkwnon connection for item status '{$item->status()}'.", $logParams);
         $continue = false;
         $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
         $flow->persist();
     }
     //
     // Checking if there's a connection configuration to analyze.
     if ($connection) {
         //
         // Checking what action should be taken.
         if (isset($connection->step) || isset($connection->status)) {
             //
             // Setting the next step to take.
             if (isset($connection->step)) {
                 $flow->step = $connection->step;
             }
             //
             // Setting new flows status.
             if (isset($connection->status)) {
                 $flow->status = $connection->status;
                 $continue = false;
             }
         } else {
             $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Neither step not status configuration for connection on status '{$item->status()}'.", $logParams);
             $continue = false;
             $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
         }
         //
         // If the flow has been set to wait, it should check if
         // the waiting has conditions.
         if ($flow->status == WKFL_ITEM_FLOW_STATUS_WAIT && isset($connection->wait)) {
             //
             // Checking configuration.
             if (!isset($connection->wait->attempts) || !isset($connection->wait->status)) {
                 $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Waiting configuration is incorrect.", $logParams);
                 $continue = false;
                 $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
             } else {
                 //
                 // Incrising the attempts count.
                 $flow->attempts++;
                 //
                 // Checking it already tried to many
                 // times.
                 if ($flow->attempts > $connection->wait->attempts) {
                     //
                     // Changing flow status and
                     // resetting counts.
                     $flow->status = $connection->wait->status;
                     $flow->attempts = 0;
                     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Maximum attempts reached ({$connection->wait->attempts} attempts), changing flow status to '{$connection->wait->status}'.", $logParams);
                 } else {
                     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Increasing attempts to {$flow->attempts}.", $logParams);
                 }
             }
         } else {
             //
             // The rest of the status keep the attempts to
             // zero.
             $flow->attempts = 0;
         }
         //
         // Saving flow status.
         $flow->persist();
         $this->_log->log(LGGR_LOG_LEVEL_INFO, "Next step: '{$flow->step}'. Next flow status: '{$flow->status}'.", $logParams);
     }
     return $continue;
 }