コード例 #1
0
 private static function pushNotification($userKey, $message, $title = null, $url = null, $urltitle = null)
 {
     Logger::getLogger('MESSAGING')->debug('Pushover[pushNotification' . ']; $userKey=[' . $userKey . ']; $message=[' . $message . ']; $title=[' . $title . ']; $url=[' . $url . ']; $urltitle=[' . $urltitle . ']');
     $notification = new Pushover();
     $token = Config::get('applicationToken', 'msg_pushover');
     if (is_null($token)) {
         throw new Exception("Pushover - Application token not specified", 500);
     }
     if (is_null($userKey)) {
         throw new Exception("Pushover - User key not specified", 500);
     }
     $notification->setToken($token);
     $notification->setUser($userKey);
     $notification->setMessage($message);
     if (!is_null($title)) {
         $notification->setTitle($title);
     }
     $notification->setHtml(1);
     $notification->setUrl($url);
     $notification->setUrlTitle($urltitle);
     if (!$notification->send()) {
         Logger::getUserLogger()->error("Pushover - Error in sending a notification to '{$userKey}'");
     } else {
         Logger::getUserLogger()->notice("Pushover message sent.");
     }
 }
コード例 #2
0
ファイル: Session.php プロジェクト: AmpersandTarski/Ampersand
 /**
  * Constructor of Session class
  * private to prevent any outside instantiation of this object
  */
 private function __construct()
 {
     $this->logger = Logger::getLogger('FW');
     $this->database = Database::singleton();
     $conceptSession = Concept::getConceptByLabel('SESSION');
     // Also checks if 'SESSION' is defined as concept in Ampersand script
     $this->id = session_id();
     $this->sessionAtom = new Atom($this->id, $conceptSession);
     $this->logger->debug("Session id: {$this->id}");
     // Remove expired Ampersand sessions from __SessionTimeout__ and all concept tables and relations where it appears.
     $expiredSessionsAtoms = array_column((array) $this->database->Exe("SELECT SESSION FROM `__SessionTimeout__` WHERE `lastAccess` < " . (time() - Config::get('sessionExpirationTime'))), 'SESSION');
     foreach ($expiredSessionsAtoms as $expiredSessionAtom) {
         if ($expiredSessionAtom == $this->id) {
             // Notify user that session is expired when login functionality is enabled
             if (Config::get('loginEnabled')) {
                 Logger::getUserLogger()->warning("Your session has expired, please login again");
             }
             // 440 Login Timeout -> is redirected by frontend to login page
         }
         $this->destroyAmpersandSession($expiredSessionAtom);
     }
     // Create a new Ampersand session atom if not yet in SESSION table (browser started a new session or Ampersand session was expired)
     $sessionAtom = new Atom($this->id, $conceptSession);
     if (!$sessionAtom->atomExists()) {
         $sessionAtom->addAtom();
         $this->database->commitTransaction();
         //TODO: ook door Database->closeTransaction() laten doen, maar die verwijst terug naar Session class voor de checkrules. Oneindige loop
     }
     $this->database->Exe("INSERT INTO `__SessionTimeout__` (`SESSION`,`lastAccess`) VALUES ('" . $this->id . "', '" . time() . "') ON DUPLICATE KEY UPDATE `lastAccess` = '" . time() . "'");
     // Add public interfaces
     $this->accessibleInterfaces = InterfaceObject::getPublicInterfaces();
 }
コード例 #3
0
 private static function pushNotification($userKey, $message, $title = null, $url = null, $urltitle = null)
 {
     Logger::getLogger('MESSAGING')->debug('Pushalot - $userKey=[' . $userKey . ']; $message=[' . $message . ']; $title=[' . $title . ']; $url=[' . $url . ']; $urltitle=[' . $urltitle . ']');
     if (is_null($userKey)) {
         throw new Exception("Pushalot - User/API key not specified", 500);
     }
     $notification = new Pushalot($userKey);
     //$pushalot->setProxy('http://localhost:12345','user:pass');
     $success = $notification->sendMessage(array('Title' => $title, 'Body' => $message, 'IsImportant' => true, 'IsSilent' => false, 'Image' => 'http://wiki.tarski.nl/skins/common/images/AmpersandLogo.png', 'Source' => 'Ampersand prototype'));
     if (!$success) {
         Logger::getUserLogger()->error("Pushalot error '{$notification->getError}()' sending notification to '{$userKey}'");
     } else {
         Logger::getUserLogger()->notice("Pushalot message sent.");
     }
 }
コード例 #4
0
function datimeGT($gtRelation, $DateConcept, $srcAtom, $tgtAtom)
{
    Logger::getLogger('EXECENGINE')->debug("datimeGT({$gtRelation},{$DateConcept},{$srcAtom},{$tgtAtom})");
    if (($dt1 = strtotime($srcAtom)) === false) {
        Logger::getUserLogger()->error("datimeGT: Illegal date {$dt1} specified in srcAtom (3rd arg): {$srcAtom}");
    }
    if (($dt2 = strtotime($tgtAtom)) === false) {
        Logger::getUserLogger()->error("datimeGT: Illegal date {$dt2} specified in tgtAtom (4th arg): {$tgtAtom}");
    }
    if ($dt1 == $dt2) {
        return;
    }
    if ($dt1 > $dt2) {
        InsPair($gtRelation, $DateConcept, $srcAtom, $DateConcept, $tgtAtom);
    } else {
        InsPair($gtRelation, $DateConcept, $tgtAtom, $DateConcept, $srcAtom);
    }
}
コード例 #5
0
ファイル: Rule.php プロジェクト: AmpersandTarski/Ampersand
 /**
  *
  * @param boolean $cacheConjuncts
  * @return Violation[]
  */
 private function checkRule($cacheConjuncts = true)
 {
     $this->logger->debug("Checking rule '{$this->id}'");
     try {
         $violations = array();
         // Evaluate conjuncts of this rule
         foreach ($this->conjuncts as $conjunct) {
             foreach ($conjunct->evaluateConjunct($cacheConjuncts) as $violation) {
                 $violations[] = new Violation($this, $violation['src'], $violation['tgt']);
             }
         }
         // If no violations => rule holds
         if (empty($violations)) {
             $this->logger->debug("Rule '{$this->id}' holds");
         }
         // Cache violations when requested
         if ($cacheConjuncts) {
             $this->violations = $violations;
         }
         return $violations;
     } catch (Exception $e) {
         Logger::getUserLogger()->error("While evaluating rule '{$this->id}': {$e->getMessage()}");
     }
 }
コード例 #6
0
 /**
  * Function to create a new Atom at the given interface.
  * @param array $data
  * @param array $options
  * @throws Exception
  * @return mixed
  */
 public function create($data, $options = array())
 {
     $this->logger->debug("create() called on {$this->path}");
     // CRUD check
     if (!$this->crudC) {
         throw new Exception("Create not allowed for '{$this->path}'", 405);
     }
     if (!$this->tgtConcept->isObject) {
         throw new Exception("Cannot create non-object '{$this->tgtConcept}' in '{$this->path}'. Use PATCH add operation instead", 405);
     }
     if ($this->isRef()) {
         throw new Exception("Cannot create on reference interface in '{$this->path}'. See #498", 501);
     }
     // Handle options
     if (isset($options['requestType'])) {
         $this->database->setRequestType($options['requestType']);
     }
     // Perform create
     $newAtom = $this->tgtConcept->createNewAtom();
     // Special case for CREATE in I[Concept] interfaces
     if ($this->srcAtom->id === '_NEW') {
         $this->srcAtom->setId($newAtom->id);
         $this->path = str_replace('_NEW', $newAtom->getJsonRepresentation(), $this->path);
     }
     // If interface expression is a relation, also add tuple(this, newAtom) in this relation
     if ($this->relation) {
         $this->relation()->addLink($this->srcAtom, $newAtom, $this->relationIsFlipped);
     } else {
         $newAtom->addAtom();
     }
     // Walk to new atom
     $newAtom = $this->atom($newAtom->id);
     // Set requested state (using patches)
     $patches = is_array($data) ? $data['patches'] : array();
     $newAtom->doPatches($patches);
     // Special case for file upload. TODO: make extension with hooks
     if ($this->tgtConcept->isFileObject()) {
         $conceptFilePath = Concept::getConceptByLabel('FilePath');
         $conceptFileName = Concept::getConceptByLabel('FileName');
         if (is_uploaded_file($_FILES['file']['tmp_name'])) {
             $tmp_name = $_FILES['file']['tmp_name'];
             $new_name = time() . '_' . $_FILES['file']['name'];
             $absolutePath = Config::get('absolutePath') . Config::get('uploadPath') . $new_name;
             $relativePath = Config::get('uploadPath') . $new_name;
             $result = move_uploaded_file($tmp_name, $absolutePath);
             if ($result) {
                 Logger::getUserLogger()->notice("File '{$new_name}' uploaded");
             } else {
                 throw new Exception("Error in file upload", 500);
             }
             // Populate filePath and originalFileName relations in database
             $relFilePath = Relation::getRelation('filePath', $newAtom->concept, $conceptFilePath);
             $relOriginalFileName = Relation::getRelation('originalFileName', $newAtom->concept, $conceptFileName);
             $relFilePath->addLink($newAtom, new Atom($relativePath, $conceptFilePath));
             $relOriginalFileName->addLink($newAtom, new Atom($_FILES['file']['name'], $conceptFileName));
         } else {
             throw new Exception("No file uploaded", 500);
         }
     }
     // Close transaction
     $this->database->closeTransaction($newAtom->concept . ' created', null, $newAtom);
     // temp store content of $newAtom (also when not crudR)
     // Return atom content (can be null)
     return $newAtom->getStoredContent();
 }
コード例 #7
0
ファイル: import.php プロジェクト: AmpersandTarski/Ampersand
global $app;
// Path to API is 'api/v1/excelimport/import'
$app->post('/excelimport/import', function () use($app) {
    $session = Session::singleton();
    $roleIds = $app->request->params('roleIds');
    $session->activateRoles($roleIds);
    // Check sessionRoles if allowedRolesForExcelImport is specified
    $allowedRoles = Config::get('allowedRolesForExcelImport', 'excelImport');
    if (!is_null($allowedRoles)) {
        $ok = false;
        foreach ($session->getSessionRoles() as $role) {
            if (in_array($role->label, $allowedRoles)) {
                $ok = true;
            }
        }
        if (!$ok) {
            throw new Exception("You do not have access to import excel files", 401);
        }
    }
    if (is_uploaded_file($_FILES['file']['tmp_name'])) {
        // Parse:
        $parser = new ExcelImport();
        $parser->ParseFile($_FILES['file']['tmp_name']);
        Database::singleton()->closeTransaction("File {$_FILES['file']['tmp_name']} imported successfully", true);
        unlink($_FILES['file']['tmp_name']);
    } else {
        Logger::getUserLogger()->error("No file uploaded");
    }
    $result = array('notifications' => Notifications::getAll(), 'files' => $_FILES);
    print json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
});
コード例 #8
0
 public static function run($allRules = false)
 {
     $database = Database::singleton();
     $logger = Logger::getLogger('EXECENGINE');
     $logger->info("ExecEngine run started");
     // Load the execEngine functions (security hazard :P)
     $files = getDirectoryList(__DIR__ . '/functions');
     foreach ($files as $file) {
         if (substr($file, -3) !== 'php') {
             continue;
         }
         require_once $path = __DIR__ . '/functions/' . $file;
         $logger->debug("Included file: {$path}");
     }
     self::$roleName = Config::get('execEngineRoleName', 'execEngine');
     try {
         $role = Role::getRoleByName(self::$roleName);
     } catch (Exception $e) {
         $logger->warning("ExecEngine extension included but role '" . self::$roleName . "' not used/defined in &-script.");
         self::$doRun = false;
         // prevent exec engine execution
     }
     $maxRunCount = Config::get('maxRunCount', 'execEngine');
     self::$runCount = 0;
     self::$autoRerun = Config::get('autoRerun', 'execEngine');
     // Get all rules that are maintained by the ExecEngine
     $rulesThatHaveViolations = array();
     while (self::$doRun) {
         self::$doRun = false;
         self::$runCount++;
         // Prevent infinite loop in ExecEngine reruns
         if (self::$runCount > $maxRunCount) {
             Logger::getUserLogger()->error('Maximum reruns exceeded for ExecEngine (rules with violations:' . implode(', ', $rulesThatHaveViolations) . ')');
             break;
         }
         $logger->notice("ExecEngine run #" . self::$runCount . " (auto rerun: " . var_export(self::$autoRerun, true) . ") for role '{$role->label}'");
         // Determine affected rules that must be checked by the exec engine
         $affectedConjuncts = RuleEngine::getAffectedConjuncts($database->getAffectedConcepts(), $database->getAffectedRelations(), 'sig');
         $affectedRules = array();
         foreach ($affectedConjuncts as $conjunct) {
             $affectedRules = array_merge($affectedRules, $conjunct->sigRuleNames);
         }
         // Check rules
         $rulesThatHaveViolations = array();
         foreach ($role->maintains() as $ruleName) {
             if (!in_array($ruleName, $affectedRules) && !$allRules) {
                 continue;
             }
             // skip this rule
             $rule = Rule::getRule($ruleName);
             $violations = $rule->getViolations(false);
             if (count($violations)) {
                 $rulesThatHaveViolations[] = $rule->id;
                 // Fix violations for every rule
                 $logger->notice("ExecEngine fixing " . count($violations) . " violations for rule '{$rule->id}'");
                 self::fixViolations($violations);
                 // Conjunct violations are not cached, because they are fixed by the ExecEngine
                 $logger->debug("Fixed " . count($violations) . " violations for rule '{$rule->__toString()}'");
                 // If $autoRerun, set $doRun to true because violations have been fixed (this may fire other execEngine rules)
                 if (self::$autoRerun) {
                     self::$doRun = true;
                 }
             }
         }
     }
     $logger->info("ExecEngine run completed");
 }
コード例 #9
0
ファイル: Email.php プロジェクト: AmpersandTarski/Ampersand
 private static function pushNotification($emailAddr, $message, $title = null, $url = null, $urltitle = null)
 {
     Logger::getLogger('MESSAGING')->debug('Email[pushNotification' . ']; $emailAddr=[' . $emailAddr . ']; $message=[' . $message . ']; $title=[' . $title . ']; $url=[' . $url . ']; $urltitle=[' . $urltitle . ']');
     // adapted from http://phpmailer.worxware.com/?pg=examplebgmail
     $config = Config::get('sendEmailConfig', 'msg_email');
     $from = $config['from'];
     $username = $config['username'];
     $password = $config['password'];
     Logger::getLogger('MESSAGING')->debug('Email.php - Username = '******'smtp.gmail.com';
     // Specify main and backup server
     $mail->SMTPSecure = 'ssl';
     // Enable encryption, 'ssl' also accepted
     $mail->Port = 465;
     $mail->SMTPAuth = true;
     // Enable SMTP authentication
     $mail->Username = $username;
     // SMTP username (for GMAIL)
     $mail->Password = $password;
     // SMTP password
     $mail->From = $from;
     $mail->FromName = 'Ampersand Prototype';
     $mail->AddAddress($emailAddr);
     // Add a recipient, e.g. $to = '*****@*****.**', 'Rieks Joosten'
     $mail->Subject = $title;
     //      $message = $message . 'optional URL';
     if ($url != '_NULL' && $url != '') {
         $mail->IsHTML(true);
         // make sure we send in HTML
         if ($urltitle != '_NULL' && $urltitle != '') {
             $message = '<p>' . $message . '</p><p><a href=' . $url . '>' . $urltitle . '</a></p>';
         } else {
             $message = $message . '<a' . $urltitle . '</a>';
         }
         Logger::getLogger('MESSAGING')->debug('Email message refactored to: [' . $message . ']');
     }
     $mail->Body = $message;
     $mail->WordWrap = 50;
     // Set word wrap to 50 characters
     if (!$mail->Send()) {
         Logger::getUserLogger()->error('Mailer Error: ' . $mail->ErrorInfo);
     } else {
         Logger::getUserLogger()->notice("Email message sent.");
     }
 }
コード例 #10
0
 /**
  * Function to evaluate conjunct
  * @param boolean $cacheConjuncts
  * @return array[] array(array('src' => '<srcAtomId>', 'tgt' => '<tgtAtomId>'))
  */
 public function evaluateConjunct($cacheConjuncts = true)
 {
     $this->logger->debug("Checking conjunct '{$this->id}' cache:" . var_export($cacheConjuncts, true));
     try {
         // If conjunct is already evaluated and conjunctCach may be used -> return violations
         if (isset($this->conjunctViolations) && $cacheConjuncts) {
             $this->logger->debug("Conjunct is already evaluated, getting violations from cache");
             return $this->conjunctViolations;
             // Otherwise evaluate conjunct, cache and return violations
         } else {
             $db = Database::singleton();
             $dbsignalTableName = Config::get('dbsignalTableName', 'mysqlDatabase');
             $violations = array();
             // Execute conjunct query
             $violations = (array) $db->Exe($this->query);
             // Cache violations in php Conjunct object
             if ($cacheConjuncts) {
                 $this->conjunctViolations = $violations;
             }
             if (count($violations) == 0) {
                 $this->logger->debug("Conjunct '{$this->id}' holds");
                 // Remove "old" conjunct violations from database
                 $query = "DELETE FROM `{$dbsignalTableName}` WHERE `conjId` = '{$this->id}'";
                 $db->Exe($query);
             } else {
                 $this->logger->debug("Conjunct '{$this->id}' broken, updating violations in database");
                 // Remove "old" conjunct violations from database
                 $query = "DELETE FROM `{$dbsignalTableName}` WHERE `conjId` = '{$this->id}'";
                 $db->Exe($query);
                 // Add new conjunct violation to database
                 $query = "INSERT IGNORE INTO `{$dbsignalTableName}` (`conjId`, `src`, `tgt`) VALUES ";
                 $values = array();
                 foreach ($violations as $violation) {
                     $values[] = "('{$this->id}', '" . $db->escape($violation['src']) . "', '" . $db->escape($violation['tgt']) . "')";
                 }
                 $query .= implode(',', $values);
                 $db->Exe($query);
             }
             return $violations;
         }
     } catch (Exception $e) {
         Logger::getUserLogger()->error("While checking conjunct '{$this->id}': " . $e->getMessage());
         return array();
     }
 }
コード例 #11
0
 /**
  * Function to request closing the open database transaction
  * @param string $succesMessage specifies success/info message when invariants hold
  * @param boolean $databaseCommit specifies to commit (true) or rollback (false) when all invariants hold
  * @param Atom $atomStoreNewContent specifies to store the new content for the updated/created atom
  * @return boolean specifies if invariant rules hold (true) or not (false)
  */
 public function closeTransaction($succesMessage = 'Updated', $databaseCommit = null, &$atomStoreNewContent = null)
 {
     Hooks::callHooks('preDatabaseCloseTransaction', get_defined_vars());
     $this->logger->info("Closing database transaction");
     $this->logger->info("Checking all affected conjuncts");
     // Check invariant rules (we only have to check the affected invariant rules)
     $affectedConjuncts = RuleEngine::getAffectedConjuncts($this->affectedConcepts, $this->affectedRelations, 'inv');
     // Get affected invariant conjuncts
     $invariantRulesHold = RuleEngine::checkInvariantRules($affectedConjuncts, true);
     // Check all process rules that are relevant for the activate roles
     RuleEngine::checkProcessRules();
     unset($this->affectedConcepts, $this->affectedRelations);
     $this->affectedConcepts = array();
     $this->affectedRelations = array();
     if (!is_null($atomStoreNewContent)) {
         $atomStoreNewContent->setStoredContent();
     }
     // Determine if transaction should be committed or not when all invariant rules hold based on $requestType
     if (is_null($databaseCommit)) {
         $databaseCommit = $this->processRequestType();
     }
     if ($invariantRulesHold && $databaseCommit) {
         $this->commitTransaction();
         // commit database transaction
         Logger::getUserLogger()->notice($succesMessage);
     } elseif (Config::get('ignoreInvariantViolations', 'transactions') && $databaseCommit) {
         $this->commitTransaction();
         Logger::getUserLogger()->warning("Transaction committed with invariant violations");
     } elseif ($invariantRulesHold) {
         $this->logger->info("Invariant rules hold, but no database commit requested");
         $this->rollbackTransaction();
         // rollback database transaction
     } else {
         $this->logger->info("Invariant rules do not hold");
         $this->rollbackTransaction();
         // rollback database transaction
     }
     Hooks::callHooks('postDatabaseCloseTransaction', get_defined_vars());
     return $this->invariantRulesHold = $invariantRulesHold;
 }
コード例 #12
0
ファイル: SMS.php プロジェクト: AmpersandTarski/Ampersand
 private static function pushNotification($SMSAddr, $message, $title = null, $url = null, $urltitle = null)
 {
     Logger::getLogger('MESSAGING')->debug('UNTESTED !!! SMS[pushNotification' . ']; $SMSAddr=[' . $SMSAddr . ']; $message=[' . $message . ']; $title=[' . $title . ']; $url=[' . $url . ']; $urltitle=[' . $urltitle . ']');
     /* Config params for SendSMS function of ExecEngine (using MessageBird.com)
      * Set the sender, could be a number (16 numbers) or letters (11 characters)
      * 
      */
     // Copy the following line to localSettings.php and provide settings
     // Config::set('sendSMSConfig', 'execEngine', array('username' => '', 'password' => '', 'sender' => ''));
     $config = Config::get('sendSMSConfig', 'msg_SMS');
     $username = $config['username'];
     $password = $config['password'];
     $sender = $config['sender'];
     Logger::getLogger('MESSAGING')->debug('Username = '******'31600000000');
     // Set an reference, optional
     // $sms->setReference('123456789');
     // Set a schedule date-time, optional
     // $sms->setTimestamp('2014-01-01 10:02');
     // Replace non GSM-7 characters by appropriate valid GSM-7 characters
     // $sms->setReplacechars(false);
     // If you want a dlr notification of the message send to another url then that you have set on the web site, you can use this parameter. Don't forget to set a reference!
     // $sms->setDlrUrl('http://www.example.com/dlr_url.php');
     // If $test is TRUE, then the message is not actually sent or scheduled, and there will be no credits deducted.
     Logger::getLogger('MESSAGING')->debug("SMS testing is set to TRUE (messages are not actually sent)");
     $sms->setTest(true);
     // Send the message to the destination(s)
     $sms->sendSms($message);
     if ($sms->getResponseCode() == "01") {
         Logger::getUserLogger()->notice("SMS message sent.");
     } else {
         Logger::getUserLogger()->error('SMS error: ' . $sms->getResponseMessage());
     }
     Logger::getLogger('MESSAGING')->debug("SMS Response: " . $sms->getResponseMessage());
     Logger::getLogger('MESSAGING')->debug("SMS Balance: " . $sms->getCreditBalance());
 }
コード例 #13
0
ファイル: Atom.php プロジェクト: AmpersandTarski/Ampersand
 /**
  * Performs given patches on atom, i.e. add, replace, remove tuples in relations
  * @param array $patches
  * @throws Exception
  * @return void
  */
 public function doPatches($patches = array())
 {
     $errorCount = 0;
     foreach ((array) $patches as $key => $patch) {
         try {
             // Check patch
             if (!array_key_exists('op', $patch)) {
                 throw new Exception("No 'op' (i.e. operation) specfied for patch #{$key}", 400);
             }
             if (!array_key_exists('path', $patch)) {
                 throw new Exception("No 'path' specfied for patch #{$key}", 400);
             }
             $atomOrIfc = $this->walkIfcPath($patch['path']);
             switch ($patch['op']) {
                 case "replace":
                     $atomOrIfc->doPatchReplace($patch);
                     break;
                 case "add":
                     $atomOrIfc->doPatchAdd($patch);
                     break;
                 case "remove":
                     $atomOrIfc->doPatchRemove($patch);
                     break;
                 default:
                     throw new Exception("Unknown patch operation '" . $patch['op'] . "'. Supported are: 'replace', 'add' and 'remove'", 501);
             }
         } catch (Exception $e) {
             Logger::getUserLogger()->error($e->getMessage());
             $errorCount++;
         }
     }
     if ($errorCount) {
         $totalPatches = count($patches);
         $processed = $totalPatches - $errorCount;
         Logger::getUserLogger()->warning("{$processed}/{$totalPatches} patches processed. {$errorCount} errors.");
     }
 }
コード例 #14
0
function ClearConcept($concept, $atom)
{
    Logger::getLogger('EXECENGINE')->info("ClearConcept({$concept},{$atom})");
    if (func_num_args() != 2) {
        throw new Exception("Wrong number of arguments supplied for function ClearConcept(): " . func_num_args() . " arguments", 500);
    }
    try {
        $database = Database::singleton();
        $atom = new Atom($atom, Concept::getConceptByLabel($concept));
        $database->atomClearConcept($atom);
        Logger::getLogger('EXECENGINE')->debug("Atom '{$atom->__toString()}' removed as member from concept '{$concept}'");
    } catch (Exception $e) {
        Logger::getUserLogger()->error('ClearConcept: ' . $e->getMessage());
    }
}