public function run($id = null) { $this->id = $id; $this->addr = gethostbyname(gethostname()); if (!Z_CONFIG::$PROCESSORS_ENABLED) { $sleep = 20; $this->log("Processors disabled — exiting in {$sleep} seconds"); sleep($sleep); try { $this->notifyProcessor("LOCK" . " " . $id); } catch (Exception $e) { $this->log($e); } return; } $this->log("Starting sync processor"); $startTime = microtime(true); try { $processed = $this->processFromQueue(); if (Zotero_DB::transactionInProgress()) { error_log("WARNING: Transaction still in progress after processing!"); } } catch (Exception $e) { $this->log($e); throw $e; } $duration = microtime(true) - $startTime; $error = false; // Success if ($processed == 1) { $this->log("Process completed in " . round($duration, 2) . " seconds"); $signal = "DONE"; } else { if ($processed == 0) { $this->log("Exiting with no processes found"); $signal = "NONE"; } else { if ($processed == -1) { $this->log("Exiting on lock error"); $signal = "LOCK"; } else { $this->log("Exiting on error"); $signal = "ERROR"; } } } if ($id) { try { $this->notifyProcessor($signal . " " . $id); } catch (Exception $e) { $this->log($e); } } }
public static function indexItem(Zotero_Item $item, $content, $stats = array()) { if (!$item->isAttachment()) { throw new Exception("Full-text content can only be added for attachments", Z_ERROR_INVALID_INPUT); } Zotero_DB::beginTransaction(); $libraryID = $item->libraryID; $key = $item->key; $version = Zotero_Libraries::getUpdatedVersion($item->libraryID); $timestamp = Zotero_DB::transactionInProgress() ? Zotero_DB::getTransactionTimestamp() : date("Y-m-d H:i:s"); // Add to MySQL for syncing, since Elasticsearch doesn't refresh immediately $sql = "REPLACE INTO itemFulltext ("; $fields = ["itemID", "version", "timestamp"]; $params = [$item->id, $version, $timestamp]; $sql .= implode(", ", $fields) . ") VALUES (" . implode(', ', array_fill(0, sizeOf($params), '?')) . ")"; Zotero_DB::query($sql, $params, Zotero_Shards::getByLibraryID($libraryID)); // Add to Elasticsearch self::indexItemInElasticsearch($libraryID, $key, $version, $timestamp, $content, $stats); Zotero_DB::commit(); }
public static function updateFromJSON(Zotero_Item $item, $json, Zotero_Item $parentItem = null, $requestParams, $userID, $requireVersion = 0, $partialUpdate = false) { $json = Zotero_API::extractEditableJSON($json); $exists = Zotero_API::processJSONObjectKey($item, $json, $requestParams); // computerProgram used 'version' instead of 'versionNumber' before v3 if ($requestParams['v'] < 3 && isset($json->version)) { $json->versionNumber = $json->version; unset($json->version); } Zotero_API::checkJSONObjectVersion($item, $json, $requestParams, $requireVersion); self::validateJSONItem($json, $item->libraryID, $exists ? $item : null, $parentItem || ($exists ? !!$item->getSourceKey() : false), $requestParams, $partialUpdate && $exists); $changed = false; $twoStage = false; if (!Zotero_DB::transactionInProgress()) { Zotero_DB::beginTransaction(); $transactionStarted = true; } else { $transactionStarted = false; } // Set itemType first if (isset($json->itemType)) { $item->setField("itemTypeID", Zotero_ItemTypes::getID($json->itemType)); } $changedDateModified = false; foreach ($json as $key => $val) { switch ($key) { case 'key': case 'version': case 'itemKey': case 'itemVersion': case 'itemType': case 'deleted': continue; case 'parentItem': $item->setSourceKey($val); break; case 'creators': if (!$val && !$item->numCreators()) { continue 2; } $orderIndex = -1; foreach ($val as $newCreatorData) { // JSON uses 'name' and 'firstName'/'lastName', // so switch to just 'firstName'/'lastName' if (isset($newCreatorData->name)) { $newCreatorData->firstName = ''; $newCreatorData->lastName = $newCreatorData->name; unset($newCreatorData->name); $newCreatorData->fieldMode = 1; } else { $newCreatorData->fieldMode = 0; } // Skip empty creators if (Zotero_Utilities::unicodeTrim($newCreatorData->firstName) === "" && Zotero_Utilities::unicodeTrim($newCreatorData->lastName) === "") { break; } $orderIndex++; $newCreatorTypeID = Zotero_CreatorTypes::getID($newCreatorData->creatorType); // Same creator in this position $existingCreator = $item->getCreator($orderIndex); if ($existingCreator && $existingCreator['ref']->equals($newCreatorData)) { // Just change the creatorTypeID if ($existingCreator['creatorTypeID'] != $newCreatorTypeID) { $item->setCreator($orderIndex, $existingCreator['ref'], $newCreatorTypeID); } continue; } // Same creator in a different position, so use that $existingCreators = $item->getCreators(); for ($i = 0, $len = sizeOf($existingCreators); $i < $len; $i++) { if ($existingCreators[$i]['ref']->equals($newCreatorData)) { $item->setCreator($orderIndex, $existingCreators[$i]['ref'], $newCreatorTypeID); continue; } } // Make a fake creator to use for the data lookup $newCreator = new Zotero_Creator(); $newCreator->libraryID = $item->libraryID; foreach ($newCreatorData as $key => $val) { if ($key == 'creatorType') { continue; } $newCreator->{$key} = $val; } // Look for an equivalent creator in this library $candidates = Zotero_Creators::getCreatorsWithData($item->libraryID, $newCreator, true); if ($candidates) { $c = Zotero_Creators::get($item->libraryID, $candidates[0]); $item->setCreator($orderIndex, $c, $newCreatorTypeID); continue; } // None found, so make a new one $creatorID = $newCreator->save(); $newCreator = Zotero_Creators::get($item->libraryID, $creatorID); $item->setCreator($orderIndex, $newCreator, $newCreatorTypeID); } // Remove all existing creators above the current index if ($exists && ($indexes = array_keys($item->getCreators()))) { $i = max($indexes); while ($i > $orderIndex) { $item->removeCreator($i); $i--; } } break; case 'tags': $item->setTags($val); break; case 'collections': $item->setCollections($val); break; case 'relations': $item->setRelations($val); break; case 'attachments': case 'notes': if (!$val) { continue; } $twoStage = true; break; case 'note': $item->setNote($val); break; // Attachment properties // Attachment properties case 'linkMode': $item->attachmentLinkMode = Zotero_Attachments::linkModeNameToNumber($val, true); break; case 'contentType': case 'charset': case 'filename': $k = "attachment" . ucwords($key); $item->{$k} = $val; break; case 'md5': $item->attachmentStorageHash = $val; break; case 'mtime': $item->attachmentStorageModTime = $val; break; case 'dateModified': $changedDateModified = $item->setField($key, $val); break; default: $item->setField($key, $val); break; } } if ($parentItem) { $item->setSource($parentItem->id); } else { if ($requestParams['v'] >= 2 && !$partialUpdate && $item->getSourceKey() && !isset($json->parentItem)) { $item->setSourceKey(false); } } $item->deleted = !empty($json->deleted); // If item has changed, update it with the current timestamp if ($item->hasChanged() && !$changedDateModified) { $item->dateModified = Zotero_DB::getTransactionTimestamp(); } $changed = $item->save($userID) || $changed; // Additional steps that have to be performed on a saved object if ($twoStage) { foreach ($json as $key => $val) { switch ($key) { case 'attachments': if (!$val) { continue; } foreach ($val as $attachmentJSON) { $childItem = new Zotero_Item(); $childItem->libraryID = $item->libraryID; self::updateFromJSON($childItem, $attachmentJSON, $item, $requestParams, $userID); } break; case 'notes': if (!$val) { continue; } $noteItemTypeID = Zotero_ItemTypes::getID("note"); foreach ($val as $note) { $childItem = new Zotero_Item(); $childItem->libraryID = $item->libraryID; $childItem->itemTypeID = $noteItemTypeID; $childItem->setSource($item->id); $childItem->setNote($note->note); $childItem->save(); } break; } } } if ($transactionStarted) { Zotero_DB::commit(); } return $changed; }
public static function updateMultipleFromJSON($json, $requestParams, $libraryID, $userID, Zotero_Permissions $permissions, $requireVersion, $parent = null) { $type = self::$objectType; $types = self::$objectTypePlural; $keyProp = $type . "Key"; switch ($type) { case 'collection': case 'search': if ($parent) { throw new Exception('$parent is not valid for ' . $type); } break; case 'item': break; default: throw new Exception("Function not valid for {$type}"); } self::validateMultiObjectJSON($json, $requestParams); $results = new Zotero_Results($requestParams); if ($requestParams['v'] >= 2 && Zotero_DB::transactionInProgress()) { throw new Exception("Transaction cannot be open when starting multi-object update"); } // If single collection object, stuff in array if ($requestParams['v'] < 2 && $type == 'collection' && !isset($json->collections)) { $json = [$json]; } else { if ($requestParams['v'] < 3) { $json = $json->{$types}; } } $i = 0; foreach ($json as $prop => $jsonObject) { Zotero_DB::beginTransaction(); try { if (!is_object($jsonObject)) { throw new Exception("Invalid value for index {$prop} in uploaded data; expected JSON {$type} object", Z_ERROR_INVALID_INPUT); } $className = "Zotero_" . ucwords($type); $obj = new $className(); $obj->libraryID = $libraryID; if ($type == 'item') { $changed = self::updateFromJSON($obj, $jsonObject, $parent, $requestParams, $userID, $requireVersion, true); } else { $changed = self::updateFromJSON($obj, $jsonObject, $requestParams, $userID, $requireVersion, true); } Zotero_DB::commit(); if ($changed) { $results->addSuccessful($i, $obj->toResponseJSON($requestParams, $permissions)); } else { $results->addUnchanged($i, $obj->key); } } catch (Exception $e) { Zotero_DB::rollback(); if ($requestParams['v'] < 2) { throw $e; } // If object key given, include that $resultKey = isset($jsonObject->{$keyProp}) ? $jsonObject->{$keyProp} : ''; $results->addFailure($i, $resultKey, $e); } $i++; } return $results->generateReport(); }
public function checkDBTransactionState() { if (Zotero_DB::transactionInProgress()) { error_log("Transaction still in progress at request end! " . "[" . $this->method . " " . $_SERVER['REQUEST_URI'] . "]"); } }
/** * @param Zotero_Collection $collection The collection object to update; * this should be either an existing * collection or a new collection * with a library assigned. * @param object $json Collection data to write * @param boolean [$requireVersion=0] See Zotero_API::checkJSONObjectVersion() * @return boolean True if the collection was changed, false otherwise */ public static function updateFromJSON(Zotero_Collection $collection, $json, $requestParams, $userID, $requireVersion = 0, $partialUpdate = false) { $json = Zotero_API::extractEditableJSON($json); $exists = Zotero_API::processJSONObjectKey($collection, $json, $requestParams); Zotero_API::checkJSONObjectVersion($collection, $json, $requestParams, $requireVersion); self::validateJSONCollection($json, $requestParams, $partialUpdate && $exists); $changed = false; if (!Zotero_DB::transactionInProgress()) { Zotero_DB::beginTransaction(); $transactionStarted = true; } else { $transactionStarted = false; } if (isset($json->name)) { $collection->name = $json->name; } if ($requestParams['v'] >= 2 && isset($json->parentCollection)) { $collection->parentKey = $json->parentCollection; } else { if ($requestParams['v'] < 2 && isset($json->parent)) { $collection->parentKey = $json->parent; } else { if (!$partialUpdate) { $collection->parent = false; } } } $changed = $collection->save() || $changed; if ($requestParams['v'] >= 2) { if (isset($json->relations)) { $changed = $collection->setRelations($json->relations, $userID) || $changed; } else { if (!$partialUpdate) { $changed = $collection->setRelations(new stdClass(), $userID) || $changed; } } } if ($transactionStarted) { Zotero_DB::commit(); } return $changed; }
/** * @param Zotero_Setting $setting The setting object to update; * this should be either an existing * setting or a new setting * with a library and name assigned. * @param object $json Setting data to write * @param boolean [$requireVersion=0] See Zotero_API::checkJSONObjectVersion() * @return boolean True if the setting was changed, false otherwise */ public static function updateFromJSON(Zotero_Setting $setting, $json, $requestParams, $userID, $requireVersion = 0) { self::validateJSONObject($setting->name, $json, $requestParams); Zotero_API::checkJSONObjectVersion($setting, $json, $requestParams, $requireVersion); $changed = false; if (!Zotero_DB::transactionInProgress()) { Zotero_DB::beginTransaction(); $transactionStarted = true; } else { $transactionStarted = false; } $setting->value = $json->value; $changed = $setting->save() || $changed; if ($transactionStarted) { Zotero_DB::commit(); } return $changed; }