/** * @param string $projectId * @param string $userId * @return array - the DTO array */ public static function encode($projectId, $userId) { $data = array(); $user = new UserProfileModel($userId); $project = new LexProjectModel($projectId); $config = JsonEncoder::encode($project->config); $config['inputSystems'] = JsonEncoder::encode($project->inputSystems); $data['config'] = $config; // comment out at the moment until a refactor can be done that is more efficient (language data in the database?) /* $interfaceLanguageCode = $project->interfaceLanguageCode; if ($user->interfaceLanguageCode) { $interfaceLanguageCode = $user->interfaceLanguageCode; } $options = self::getInterfaceLanguages(APPPATH . 'angular-app/languageforge/lexicon/lang'); asort($options); // sort by language name $selectInterfaceLanguages = array( 'optionsOrder' => array_keys($options), 'options' => $options ); $data['interfaceConfig'] = array('userLanguageCode' => $interfaceLanguageCode); $data['interfaceConfig']['selectLanguages'] = $selectInterfaceLanguages; */ // a stand in for the code above $data['interfaceConfig'] = array('userLanguageCode' => 'en', 'selectLanguages' => array('options' => array('en' => 'English'), 'optionsOrder' => array('en'))); $optionlistListModel = new LexOptionListListModel($project); $optionlistListModel->read(); $data['optionlists'] = $optionlistListModel->entries; if ($project->hasSendReceive()) { $data['sendReceive'] = array(); $data['sendReceive']['status'] = SendReceiveCommands::getProjectStatus($projectId); } return $data; }
public static function run($mode = 'test') { $testMode = $mode != 'run'; print "Fix Meaning->Definition and Example->Sentence labels.\n"; $projectlist = new ProjectListModel(); $projectlist->read(); $fixCount = 0; $definitionLabelsUpdated = 0; $sentenceLabelsUpdated = 0; foreach ($projectlist->entries as $projectParams) { // foreach existing project $projectId = $projectParams['id']; $project = new ProjectModel($projectId); if ($project->appName == 'lexicon') { $project = new LexProjectModel($projectId); $projectChanged = false; $entryFieldsArray = $project->config->entry->fields->getArrayCopy(); if (array_key_exists("senses", $entryFieldsArray)) { $senseFieldsArray = $entryFieldsArray["senses"]->fields->getArrayCopy(); if ($senseFieldsArray["definition"]->label != "Definition") { $senseFieldsArray["definition"]->label = "Definition"; //print " Fixed \"Definition\" label\n"; $definitionLabelsUpdated++; $projectChanged = true; } if (array_key_exists("examples", $senseFieldsArray)) { $exampleFieldsArray = $senseFieldsArray["examples"]->fields->getArrayCopy(); if ($exampleFieldsArray["sentence"]->label != "Sentence") { $exampleFieldsArray["sentence"]->label = "Sentence"; //print " Fixed \"Sentence\" label\n"; $sentenceLabelsUpdated++; $projectChanged = true; } } } $senseFieldsArray["examples"]->fields->exchangeArray($exampleFieldsArray); $entryFieldsArray["senses"]->fields->exchangeArray($senseFieldsArray); $project->config->entry->fields->exchangeArray($entryFieldsArray); if ($projectChanged) { $fixCount++; if (!$testMode) { print " Saving changes to project {$project->projectName}.\n"; $project->write(); } } unset($exampleFieldsArray); unset($senseFieldsArray); unset($entryFieldsArray); } } if ($fixCount > 0) { print "{$fixCount} projects were fixed\n"; print "{$definitionLabelsUpdated} \"meaning\" labels changed to \"definition\"\n"; print "{$sentenceLabelsUpdated} \"example\" labels changed to \"sentence\"\n"; } else { print "No projects needed fixing\n"; } }
/** * @param LexProjectModel $project * @param int $newerThanTimestamp */ public function __construct($project, $newerThanTimestamp = null) { if (!is_null($newerThanTimestamp)) { $startDate = new UTCDateTime(1000 * $newerThanTimestamp); parent::__construct(self::mapper($project->databaseName()), array('dateModified' => array('$gte' => $startDate)), array()); } else { parent::__construct(self::mapper($project->databaseName()), array('name' => array('$regex' => ''))); } }
/** * Update Role Views and User Views for each custom field * Designed to be externally called (e.g. from LfMerge) * * @param string $projectCode * @param array<string> $customFieldSpecs * @return bool|string returns the project id on success, false otherwise */ public static function updateCustomFieldViews($projectCode, $customFieldSpecs) { $project = new LexProjectModel(); if (!$project->readByProperty('projectCode', $projectCode)) { return false; } self::removeDeletedCustomFieldViews($customFieldSpecs, $project->config); foreach ($customFieldSpecs as $customFieldSpec) { self::createNewCustomFieldViews($customFieldSpec['fieldName'], $customFieldSpec['fieldType'], $project->config); } return $project->write(); }
/** * Reads a MultiParagraph from the XmlNode $sxeNode given by the element 'form' * * @param \SimpleXMLElement $sxeNode * @return LexMultiParagraph */ public function readMultiParagraph($sxeNode) { // paragraph separator character U+2029 $paraSeparator = mb_convert_encoding('
', 'UTF-8', 'HTML-ENTITIES'); $multiParagraph = new LexMultiParagraph(); $this->addAndPushSubnodeError(LiftImportNodeError::MULTIPARAGRAPH, $sxeNode->getName()); /** @var \SimpleXMLElement $element */ foreach ($sxeNode as $element) { switch ($element->getName()) { case 'form': $inputSystemTag = (string) $element['lang']; $multiParagraph->inputSystem = $inputSystemTag; $value = self::sanitizeSpans(dom_import_simplexml($element->{'text'}[0]), $inputSystemTag, $this->currentNodeError()); foreach (explode($paraSeparator, $value) as $content) { $paragraph = new LexParagraph(); if ($content) { $paragraph->content = $content; } $multiParagraph->paragraphs->append($paragraph); } $this->project->addInputSystem($inputSystemTag); break; default: $this->currentNodeError()->addUnhandledElement($element->getName()); } } array_pop($this->nodeErrors); return $multiParagraph; }
/** * @param ProjectModel|LexProjectModel $projectModel * @param string $id */ public function __construct($projectModel, $id = '') { $this->setReadOnlyProp('authorInfo'); $this->setReadOnlyProp('replies'); $this->setReadOnlyProp('score'); $this->setReadOnlyProp('status'); $this->setPrivateProp('isDeleted'); $this->id = new Id(); $this->entryRef = new IdReference(); $this->isDeleted = false; $this->replies = new ArrayOf(function () { return new LexCommentReply(); }); $this->status = self::STATUS_OPEN; $this->score = 0; $this->authorInfo = new LexAuthorInfo(); $this->regarding = new LexCommentFieldReference(); $databaseName = $projectModel->databaseName(); parent::__construct(self::mapper($databaseName), $id); }
public function run($userId, $mode = 'test') { $testMode = $mode != 'run'; $message = "Remove Environment and Reversal Entries from field order config\n\n"; // Note: LF projects that don't have config data in mongo will use the default PHP model. // In these cases, the migration script won't find these field orders to remove, so nothing gets written out $projectlist = new ProjectListModel(); $projectlist->read(); $projectsAffected = 0; foreach ($projectlist->entries as $projectParams) { // foreach existing project $projectId = $projectParams['id']; $project = new ProjectModel($projectId); if ($project->appName == 'lexicon') { $project = new LexProjectModel($projectId); $fieldOrderUpdated = 0; $this->RemoveFromArray("environments", $project->config->entry->fieldOrder, $message, $fieldOrderUpdated); $fieldsArray = $project->config->entry->fields->getArrayCopy(); if (array_key_exists("senses", $fieldsArray)) { $this->RemoveFromArray("reversalEntries", $fieldsArray["senses"]->fieldOrder, $message, $fieldOrderUpdated); $project->config->entry->fields->exchangeArray($fieldsArray); } if ($fieldOrderUpdated > 0) { $projectsAffected++; $message .= "\tRemoved: {$fieldOrderUpdated} field orders from {$project->projectName}\n"; if (!$testMode) { $message .= "\tSaving changes to project {$project->projectName}.\n\n"; $project->write(); } } } unset($project); } if ($projectsAffected > 0) { $message .= "{$projectsAffected} projects were fixed\n"; } else { $message .= "No projects needed fixing\n"; } return $message; }
/** * * @param string $zipFilePath * @param LexProjectModel $projectModel * @param string $mergeRule (use LiftMergeRule const) * @param boolean $skipSameModTime * @param boolean $deleteMatchingEntry * @throws \Exception * @return LiftImport */ public function importZip($zipFilePath, $projectModel, $mergeRule = LiftMergeRule::IMPORT_WINS, $skipSameModTime = false, $deleteMatchingEntry = false) { $assetsFolderPath = $projectModel->getAssetsFolderPath(); $extractFolderPath = $assetsFolderPath . '/initialUpload_' . mt_rand(); $this->report = new ImportErrorReport(); $zipNodeError = new ZipImportNodeError(ZipImportNodeError::FILE, basename($zipFilePath)); try { self::extractZip($zipFilePath, $extractFolderPath); // Now find the .lift file in the uploaded zip $dirIter = new \RecursiveDirectoryIterator($extractFolderPath); $iterIter = new \RecursiveIteratorIterator($dirIter); $liftIter = new \RegexIterator($iterIter, '/\\.lift$/', \RegexIterator::MATCH); $liftFilePaths = array(); foreach ($liftIter as $file) { $liftFilePaths[] = $file->getPathname(); } if (empty($liftFilePaths)) { throw new \Exception("Uploaded file does not contain any LIFT data"); } if (count($liftFilePaths) > 1) { foreach (array_slice($liftFilePaths, 1) as $filename) { $zipNodeError->addUnhandledLiftFile(basename($filename)); } } // Import subfolders foreach (glob($extractFolderPath . '/*', GLOB_ONLYDIR) as $folderPath) { $folderName = basename($folderPath); switch ($folderName) { case 'pictures': case 'audio': case 'others': case 'WritingSystems': $assetsPath = $assetsFolderPath . "/" . $folderName; if (file_exists($folderPath) && is_dir($folderPath)) { FileUtilities::copyFolderTree($folderPath, $assetsPath); } break; default: $zipNodeError->addUnhandledSubfolder($folderName); } } // Import first .lift file (only). $this->liftFilePath = $liftFilePaths[0]; $this->merge($this->liftFilePath, $projectModel, $mergeRule, $skipSameModTime, $deleteMatchingEntry); if ($zipNodeError->hasError()) { error_log($zipNodeError->toString() . "\n"); } foreach ($this->report->nodeErrors as $subnodeError) { $zipNodeError->addSubnodeError($subnodeError); } $this->report = new ImportErrorReport(); $this->report->nodeErrors[] = $zipNodeError; } catch (\Exception $e) { if ($zipNodeError->hasError()) { error_log($zipNodeError->toString() . "\n"); } foreach ($this->report->nodeErrors as $subnodeError) { $zipNodeError->addSubnodeError($subnodeError); } $this->report = new ImportErrorReport(); $this->report->nodeErrors[] = $zipNodeError; throw new \Exception($e->getMessage(), $e->getCode()); } return $this; }
/** * Import a LIFT file * * @param string $projectId * @param string $mediaType * @param string $tmpFilePath * @throws \Exception * @return UploadResponse */ public static function importLiftFile($projectId, $mediaType, $tmpFilePath) { if ($mediaType != 'import-lift') { throw new \Exception("Unsupported upload type."); } if (!$tmpFilePath) { throw new \Exception("Upload controller did not move the uploaded file."); } $file = $_FILES['file']; $fileName = $file['name']; $mergeRule = $_POST['mergeRule']; $skipSameModTime = $_POST['skipSameModTime']; $deleteMatchingEntry = $_POST['deleteMatchingEntry']; $finfo = finfo_open(FILEINFO_MIME_TYPE); $fileType = finfo_file($finfo, $tmpFilePath); finfo_close($finfo); $fileName = FileUtilities::replaceSpecialCharacters($fileName); $fileExt = false === ($pos = strrpos($fileName, '.')) ? '' : substr($fileName, $pos); $allowedTypes = array("text/xml", "application/xml"); $response = new UploadResponse(); if (in_array(strtolower($fileType), $allowedTypes) && in_array(strtolower($fileExt), self::$allowedLiftExtensions)) { // make the folders if they don't exist $project = new LexProjectModel($projectId); $project->createAssetsFolders(); $folderPath = $project->getAssetsFolderPath(); $importer = LiftImport::get()->merge($tmpFilePath, $project, $mergeRule, $skipSameModTime, $deleteMatchingEntry); $project->write(); $moveOk = true; if (!$project->liftFilePath || $mergeRule != LiftMergeRule::IMPORT_LOSES) { // cleanup previous files of any allowed extension $cleanupFiles = glob($folderPath . '/*[' . implode(', ', self::$allowedLiftExtensions) . ']'); foreach ($cleanupFiles as $cleanupFile) { @unlink($cleanupFile); } // move uploaded LIFT file from tmp location to assets $filePath = $folderPath . DIRECTORY_SEPARATOR . $fileName; $project->liftFilePath = $filePath; $project->write(); $moveOk = copy($tmpFilePath, $filePath); @unlink($tmpFilePath); } // construct server response if ($moveOk && $tmpFilePath) { $data = new ImportResult(); $data->path = $project->getAssetsRelativePath(); $data->fileName = $fileName; $data->stats = $importer->stats; $data->importErrors = $importer->getReport()->toFormattedString(); $response->result = true; } else { $data = new ErrorResult(); $data->errorType = 'UserMessage'; $data->errorMessage = "{$fileName} could not be saved to the right location. Contact your Site Administrator."; $response->result = false; } } else { $allowedExtensionsStr = implode(", ", self::$allowedLiftExtensions); $data = new ErrorResult(); $data->errorType = 'UserMessage'; if (count(self::$allowedLiftExtensions) < 1) { $data->errorMessage = "{$fileName} of type: {$fileType} is not an allowed LIFT file. No LIFT file formats are currently enabled, contact your Site Administrator."; } elseif (count(self::$allowedLiftExtensions) == 1) { $data->errorMessage = "{$fileName} of type: {$fileType} is not an allowed LIFT file. Ensure the file is a {$allowedExtensionsStr}."; } else { $data->errorMessage = "{$fileName} of type: {$fileType} is not an allowed LIFT file. Ensure the file is one of the following types: {$allowedExtensionsStr}."; } $response->result = false; } $response->data = $data; return $response; }
public function run($userId, $mode = 'test') { $testMode = $mode != 'run'; $message = "Fix Lexicon View Settings (except Environments and ReversalEntries) to default to visible\n\n"; $fixCount = 0; $projectlist = new ProjectListModel(); $projectlist->read(); foreach ($projectlist->entries as $projectParams) { // foreach existing project $projectId = $projectParams['id']; $project = new ProjectModel($projectId); if ($project->appName == 'lexicon') { $project = new LexProjectModel($projectId); //$message .= "\nInspecting project $project->projectName.\n"; $showFieldUpdated = 0; $roleShowFieldUpdated = 0; //$hideFieldUpdated = 0; $roleHideFieldUpdated = 0; $disabledFields = array("environments", "reversalEntries"); foreach ($project->config->roleViews as $role => $roleView) { foreach ($roleView->fields as $fieldName => $field) { if (in_array($fieldName, $disabledFields)) { if ($field->show) { // Hide disabled fields //$message .= "Hiding role $role view of $fieldName\n"; $field->show = false; $showFieldUpdated++; $roleHideFieldUpdated++; } } else { if (!$field->show) { // enable all other fields by default $field->show = true; $showFieldUpdated++; $roleShowFieldUpdated++; } } } } $userShowFieldUpdated = 0; $userHideFieldUpdated = 0; foreach ($project->config->userViews as $userId => $userView) { foreach ($userView->fields as $fieldName => $field) { if (in_array($fieldName, $disabledFields)) { if ($field->show) { // Hide disabled fields //$message .= "Hiding user $userId view of $fieldName\n"; $field->show = false; $showFieldUpdated++; $userHideFieldUpdated++; } } else { if (!$field->show) { // enable all other fields by default $field->show = true; $showFieldUpdated++; $userShowFieldUpdated++; } } } } if ($showFieldUpdated > 0) { $fixCount++; $message .= " Toggled {$showFieldUpdated} View Settings fields. This comprised: \n"; if ($roleShowFieldUpdated > 0) { $message .= " - Changed {$roleShowFieldUpdated} role-based View Settings fields to be visible.\n"; } if ($userShowFieldUpdated > 0) { $message .= " - Changed {$userShowFieldUpdated} user-based View Settings fields to be visible.\n"; } if ($roleHideFieldUpdated > 0) { $message .= " - Changed {$roleHideFieldUpdated} role-based View Settings fields to be invisible.\n"; } if ($userHideFieldUpdated > 0) { $message .= " - Changed {$userHideFieldUpdated} user-based View Settings fields to be invisible.\n"; } if (!$testMode) { $message .= " Saving changes to project {$project->projectName}.\n"; $project->write(); } } else { //$message .= " No invisible View Settings fields found/changed.\n"; } } } if ($fixCount > 0) { $message .= "{$fixCount} projects were fixed\n"; } else { $message .= "No projects needed fixing\n"; } return $message; }
public function testCreateProject_NewSRProject_SRProjectWithLinks() { self::$environ = new LexiconMongoTestEnvironment(); self::$environ->clean(); $user1Id = self::$environ->createUser("user1name", "User1 Name", "*****@*****.**"); $user1 = new UserModel($user1Id); $srProject = array('identifier' => 'srIdentifier', 'name' => 'srName', 'repository' => 'http://public.languagedepot.org', 'role' => 'manager'); $projectId = ProjectCommands::createProject(SF_TESTPROJECT, SF_TESTPROJECTCODE, LexProjectModel::LEXICON_APP, $user1->id->asString(), self::$environ->website, $srProject); $project = new LexProjectModel($projectId); $assetImagePath = $project->getImageFolderPath(); $assetAudioPath = $project->getAudioFolderPath(); $this->assertTrue($project->hasSendReceive()); $this->assertTrue(is_link($assetImagePath)); $this->assertTrue(is_link($assetAudioPath)); $projectWorkPath = $project->getSendReceiveWorkFolder(); FileUtilities::removeFolderAndAllContents($project->getAssetsFolderPath()); FileUtilities::removeFolderAndAllContents($projectWorkPath); }
$text1 = TextCommands::updateText($testProjectId, array('id' => '', 'title' => $constants['testText1Title'], 'content' => $constants['testText1Content'])); $text2 = TextCommands::updateText($testProjectId, array('id' => '', 'title' => $constants['testText2Title'], 'content' => $constants['testText2Content'])); $question1 = QuestionCommands::updateQuestion($testProjectId, array('id' => '', 'textRef' => $text1, 'title' => $constants['testText1Question1Title'], 'description' => $constants['testText1Question1Content'])); $question2 = QuestionCommands::updateQuestion($testProjectId, array('id' => '', 'textRef' => $text1, 'title' => $constants['testText1Question2Title'], 'description' => $constants['testText1Question2Content'])); $template1 = QuestionTemplateCommands::updateTemplate($testProjectId, array('id' => '', 'title' => 'first template', 'description' => 'not particularly interesting')); $template2 = QuestionTemplateCommands::updateTemplate($testProjectId, array('id' => '', 'title' => 'second template', 'description' => 'not entirely interesting')); $answer1 = QuestionCommands::updateAnswer($testProjectId, $question1, array('id' => '', 'content' => $constants['testText1Question1Answer']), $managerUserId); $answer1Id = array_keys($answer1)[0]; $answer2 = QuestionCommands::updateAnswer($testProjectId, $question2, array('id' => '', 'content' => $constants['testText1Question2Answer']), $managerUserId); $answer2Id = array_keys($answer2)[0]; $comment1 = QuestionCommands::updateComment($testProjectId, $question1, $answer1Id, array('id' => '', 'content' => $constants['testText1Question1Answer1Comment']), $managerUserId); $comment2 = QuestionCommands::updateComment($testProjectId, $question2, $answer2Id, array('id' => '', 'content' => $constants['testText1Question2Answer2Comment']), $managerUserId); } elseif ($site == 'languageforge') { // Set up LanguageForge E2E test envrionment here ProjectCommands::updateUserRole($testProjectId, $observerUserId, LexRoles::OBSERVER); $testProjectModel = new LexProjectModel($testProjectId); $testProjectModel->addInputSystem('th-fonipa', 'tipa', 'Thai'); $testProjectModel->config->entry->fields[LexConfig::LEXEME]->inputSystems[] = 'th-fonipa'; $testProjectModel->addInputSystem('th-Zxxx-x-audio', 'taud', 'Thai Voice'); $testProjectModel->config->entry->fields[LexConfig::LEXEME]->inputSystems[] = 'th-Zxxx-x-audio'; $testProjectId = $testProjectModel->write(); // setup to mimic file upload $fileName = $constants['testEntry1']['lexeme']['th-Zxxx-x-audio']['value']; $file = array(); $file['name'] = $fileName; $_FILES['file'] = $file; // put a copy of the test file in tmp $tmpFilePath = sys_get_temp_dir() . "/CopyOf{$fileName}"; copy(TestPath . "php/common/{$fileName}", $tmpFilePath); $response = LexUploadCommands::uploadAudioFile($testProjectId, 'audio', $tmpFilePath); // cleanup tmp file if it still exists
/** * @param string $projectId * @param string $userId * @param null $lastFetchTime * @param int $offset * @throws \Exception * @return array */ public static function encode($projectId, $userId, $lastFetchTime = null, $offset = 0) { $data = array(); $project = new LexProjectModel($projectId); if ($lastFetchTime) { $entriesModel = new LexEntryListModel($project, $lastFetchTime); $entriesModel->readAsModels(); $commentsModel = new LexCommentListModel($project, $lastFetchTime); $commentsModel->readAsModels(); } else { $entriesModel = new LexEntryListModel($project, null, self::MAX_ENTRIES_PER_REQUEST, $offset); $entriesModel->readAsModels(); $commentsModel = new LexCommentListModel($project, null, self::MAX_ENTRIES_PER_REQUEST, $offset); $commentsModel->readAsModels(); $data['itemTotalCount'] = $entriesModel->totalCount > $commentsModel->totalCount ? $entriesModel->totalCount : $commentsModel->totalCount; $data['itemCount'] = $entriesModel->count > $commentsModel->count ? $entriesModel->count : $commentsModel->count; $data['offset'] = $offset; } $entries = LexDbeDtoEntriesEncoder::encode($entriesModel); $entries = $entries['entries']; $encodedComments = LexDbeDtoCommentsEncoder::encode($commentsModel); $data['comments'] = $encodedComments['entries']; $votes = new UserGenericVoteModel($userId, $projectId, 'lexCommentPlusOne'); $votesDto = array(); foreach ($votes->votes as $vote) { $votesDto[$vote->ref->id] = true; } $data['commentsUserPlusOne'] = $votesDto; if (!is_null($lastFetchTime)) { $deletedEntriesModel = new LexDeletedEntryListModel($project, $lastFetchTime); $deletedEntriesModel->read(); $data['deletedEntryIds'] = array_map(function ($e) { return $e['id']; }, $deletedEntriesModel->entries); $deletedCommentsModel = new LexDeletedCommentListModel($project, $lastFetchTime); $deletedCommentsModel->read(); $data['deletedCommentIds'] = array_map(function ($c) { return $c['id']; }, $deletedCommentsModel->entries); } $lexemeInputSystems = $project->config->entry->fields[LexConfig::LEXEME]->inputSystems; usort($entries, function ($a, $b) use($lexemeInputSystems) { $lexeme1Value = ''; if (array_key_exists(LexConfig::LEXEME, $a)) { $lexeme1 = $a[LexConfig::LEXEME]; foreach ($lexemeInputSystems as $ws) { if (array_key_exists($ws, $lexeme1) && $lexeme1[$ws]['value'] != '') { $lexeme1Value = $lexeme1[$ws]['value']; // '\P{xx} matches all characters without the Unicode property XX. L is the Unicode property "letter". $lexeme1Value = preg_replace('/^\\P{L}+/', '', $lexeme1Value); // Strip non-letter characters from front of word for sorting break; } } } $lexeme2Value = ''; if (array_key_exists(LexConfig::LEXEME, $b)) { $lexeme2 = $b[LexConfig::LEXEME]; foreach ($lexemeInputSystems as $ws) { if (array_key_exists($ws, $lexeme2) && $lexeme2[$ws]['value'] != '') { $lexeme2Value = $lexeme2[$ws]['value']; $lexeme2Value = preg_replace('/^\\P{L}+/', '', $lexeme2Value); // Strip non-letter characters from front of word for sorting break; } } } return strtolower($lexeme1Value) > strtolower($lexeme2Value) ? 1 : -1; }); $data['entries'] = $entries; $data['timeOnServer'] = time(); // for offline syncing if ($project->hasSendReceive()) { $data['sendReceive'] = array(); $data['sendReceive']['status'] = SendReceiveCommands::getProjectStatus($projectId); } return $data; }
/** * @param $identifier * @return string|bool $projectId if send receive project is linked to a LF project, false otherwise */ public static function getProjectIdFromSendReceive($identifier) { $project = new LexProjectModel(); if (!$project->readByProperty('sendReceiveProjectIdentifier', $identifier)) { return false; } return $project->id->asString(); }