Пример #1
0
 /**
  * build an array of collection data from XML, so that it may be fed to 
  * syncDBCollection()
  * if a single xsi:nil element is found, then return an empty array, 
  * signifying deletion of all elements
  * if no element is found, then return null, signifying no changes to 
  * existing elements
  * otherwise, return an array of new element so that they may be synced 
  * with the existing ones (i.e. add/remove accordingly)
  *
  * @xml SimpleXMLElement the resource's XML representation root element
  * @path string XPath to the elements that belong to the collection in 
  * question
  * @key string the key that will be used to put data in the array
  *
  * @return string[]
  * @access protected
  */
 protected function buildCollection($xml, $path, $key)
 {
     $xmli = @$xml->xpath($path);
     if ($xmli === false) {
         return null;
     }
     if (count($xmli) === 1 && $xmli[0]->attributes(RestAPIHelper::XMLNS_XSI())->nil && strval($xmli[0]->attributes(RestAPIHelper::XMLNS_XSI())->nil) == "true") {
         return array();
     } elseif (count($xmli) === 0) {
         return null;
     } else {
         $data = array();
     }
     $i = 0;
     foreach ($xmli as $xml) {
         if ($xml->attributes(RestAPIHelper::XMLNS_XSI())->nil && strval($xml->attributes(RestAPIHelper::XMLNS_XSI())->nil) != "true" || is_null($xml->attributes(RestAPIHelper::XMLNS_XSI())->nil)) {
             if ($key == "mw") {
                 $id = '{"name": "' . str_replace('"', '\'', strval($xml)) . '", "link": "' . strval($xml->attributes()->link) . '"}';
             } elseif ($key == "url") {
                 $id = '{"id": "' . strval($xml->attributes()->id) . '", "type": "' . strval($xml->attributes()->type) . '", "url": "' . strval($xml) . '", "title": "' . strval($xml->attributes()->title) . '"}';
             } elseif ($key == "license") {
                 $lic = array("licenseid" => "", "name" => "", "group" => "", "title" => "", "url" => "", "comment" => "");
                 foreach ($xml->attributes() as $lk => $lv) {
                     $lic[strval($lk)] = strval($lv);
                 }
                 $cv = $xml->attributes()->id;
                 $lic["licenseid"] = trim(strval($cv));
                 if ($lic["licenseid"] === "") {
                     $lic["licenseid"] = "0";
                 }
                 $cv = $xml->xpath("./license:comment");
                 $lic["comment"] = count($cv) > 0 ? strval($cv[0]) : "";
                 $cv = $xml->xpath("./license:url");
                 $lic["url"] = count($cv) > 0 ? strval($cv[0]) : "";
                 $cv = $xml->xpath("./license:title");
                 $lic["title"] = count($cv) > 0 ? strval($cv[0]) : "";
                 $id = json_encode($lic);
             } else {
                 $id = strval($xml->attributes()->id);
             }
             if ($id != "") {
                 $data[$key . $i] = $id;
                 $i = $i + 1;
             }
         }
     }
     return $data;
 }
Пример #2
0
 /**  
  * implementation of abstract parse() operation from RestXMLParser.
  *
  * @xml SimpleXMLElement the root element of the application XML representation
  * 
  * @return Default_Model_Researcher
  * @access public
  */
 public function parse($xml)
 {
     if (!is_null($this->_user)) {
         $person = new Default_Model_Researcher();
         try {
             $xml = new SimpleXMLElement($xml);
         } catch (Exception $e) {
             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
             $this->_extError = $e->getMessage();
             return $person;
         }
         $xmli = $xml->xpath('//person:person');
         if (count($xmli) === 0) {
             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
             return $person;
         }
         $xml = $xmli[0];
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             if ($xml->attributes()->id) {
                 $person->id = strval($xml->attributes()->id);
             } else {
                 $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
                 $this->_extError = 'Resource ID missing';
                 return $person;
             }
         }
         if ($xml->attributes()->nodissemination) {
             $person->noDissemination = strval($xml->attributes()->nodissemination) === "true" ? true : false;
         }
         if ($xml->attributes()->cname) {
             $person->cname = strval($xml->attributes()->cname);
         }
         $firstname = $this->el($xml, "person:firstname");
         if (!is_null($firstname) && trim(strval($firstname)) !== "") {
             $person->firstName = trim(strval($firstname));
         }
         $lastname = $this->el($xml, "person:lastname");
         if (!is_null($lastname) && trim(strval($lastname)) !== "") {
             $person->lastName = trim(strval($lastname));
         }
         $gender = $this->el($xml, "person:gender");
         if (!is_null($gender)) {
             if (trim(strval($gender->attributes(RestAPIHelper::XMLNS_XSI())->nil)) === "true") {
                 $person->gender = 'n/a';
             } elseif (trim(strval($gender)) !== "") {
                 if (trim(strtolower(strval($gender))) === "male") {
                     $person->gender = "male";
                 } elseif (trim(strtolower(strval($gender))) === "female") {
                     $person->gender = "female";
                 }
             }
         }
         if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
             $person->dateInclusion = date("Y-m-d");
             $person->addedByID = $this->_parent->getUser()->id;
         }
         $person->lastUpdated = date('Y-m-d');
         $institute = trim(strval($this->el($xml, "person:institute")));
         if (!is_null($institute)) {
             $person->institution = trim(strval($institute));
         }
         $country = $this->el($xml, "regional:country");
         if (!is_null($country) && trim(strval($country->attributes()->id)) !== "") {
             $person->countryID = trim(strval($country->attributes()->id));
         }
         $role = $this->el($xml, "person:role");
         if (!is_null($role) && trim(strval($role->attributes()->id)) !== "") {
             $person->positionTypeID = trim(strval($role->attributes()->id));
         }
         $image = $this->el($xml, "person:image");
         $removeImageCache = false;
         if (!is_null($image)) {
             if (trim(strval($image->attributes(RestAPIHelper::XMLNS_XSI())->nil)) === "true") {
                 $person->clearImage();
                 $removeImageCache = true;
             } else {
                 if (!is_null($image->attributes()->type) && trim(strval($image->attributes()->type)) === "base64") {
                     // image is given as byte64 encoded string
                     if (trim(strval($image)) != '') {
                         $person->image = pg_escape_bytea(trim(strval($image)));
                         $removeImageCache = true;
                     }
                 } else {
                     // image is given as URL
                     if (trim(parse_url(strval($image), PHP_URL_SCHEME)) == '') {
                         // no URL scheme present; assume uploaded file though
                         // portal's uploadimage action in AppsController
                         if (trim(strval($image)) != '') {
                             try {
                                 $person->image = pg_escape_bytea(base64_encode(file_get_contents(APPLICATION_PATH . "/../public/" . trim(strval($image)))));
                                 $removeImageCache = true;
                             } catch (Exception $e) {
                                 $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                                 $this->_extError = $e->getMessage();
                                 return $person;
                             }
                         }
                     } else {
                         // URL scheme present; assume remote file
                         if (trim(strval($image)) != '') {
                             try {
                                 $person->image = pg_escape_bytea(base64_encode(file_get_contents(trim(strval($image)))));
                                 $removeImageCache = true;
                             } catch (Exception $e) {
                                 $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                                 $this->_extError = $e->getMessage();
                                 return $person;
                             }
                         }
                     }
                 }
             }
         }
         if ($removeImageCache === true) {
             if ($person->id != '' && file_exists(APPLICATION_PATH . "/../cache/ppl-image-" . $person->id . ".png")) {
                 unlink(APPLICATION_PATH . "/../cache/ppl-image-" . $person->id . ".png");
             }
         }
         $person->save();
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             //remove existing contact info
             $conts = new Default_Model_Contacts();
             $conts->filter->researcherid->equals($person->id);
             $conts->refresh();
             for ($i = count($conts->items) - 1; $i >= 0; $i--) {
                 $conts->remove($conts->items[$i]);
             }
         }
         //add new contact info
         $cts = new Default_Model_ContactTypes();
         $cts->refresh();
         $xmli = $xml->xpath("//person:contact");
         $conts2 = new Default_Model_Contacts();
         foreach ($xmli as $x) {
             if (trim(strval($x)) !== '') {
                 $cont = new Default_Model_Contact();
                 $cont->researcherID = $person->id;
                 $ct = trim(strval($x->attributes()->type));
                 $ctid = null;
                 for ($i = 0; $i < count($cts->items); $i++) {
                     if (strtolower($ct) == strtolower($cts->items[$i]->description)) {
                         $ctid = $cts->items[$i]->id;
                         break;
                     }
                 }
                 if (!is_null($ctid)) {
                     $cont->contactTypeID = $ctid;
                 } else {
                     $cont->contactTypeID = 7;
                     //e-mail by default
                 }
                 $cont->data = trim(strval($x));
                 if (strval($x->attributes()->primary) === "true") {
                     $cont->isPrimary = true;
                 }
                 $conts2->filter->data->equals($cont->data)->and($conts2->filter->contacttypeid->equals(7))->and($conts2->filter->researcherid->notequals($person->id));
                 $conts2->refresh("xml");
                 if (count($conts2->items) == 0) {
                     $cont->save();
                 } else {
                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                     $this->_extError = "e-mail address `" . $cont->data . "' already exists";
                     return $person;
                 }
             }
         }
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST || $this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
             $xrels = $xml->xpath("person:relation");
             $ps = new Default_Model_Researchers();
             $ps->filter->id->equals($person->id);
             $p = null;
             if (count($ps->items) > 0) {
                 $p = $ps->items[0];
             }
             if ($p !== null) {
                 $rels = array();
                 if (count($xml->xpath('person:relation[@xsi:nil="true"]')) === 0) {
                     foreach ($xrels as $x) {
                         $targuid = trim(strval($x->attributes()->targetguid));
                         $subguid = trim(strval($x->attributes()->subjectguid));
                         $rel = array("id" => trim(strval($x->attributes()->id)), "parentid" => trim(strval($x->attributes()->parentid)));
                         if ($targuid === "") {
                             $rel["subjectguid"] = $subguid;
                         } else {
                             if ($subguid === "") {
                                 $rel["targetguid"] = $targuid;
                             }
                         }
                         if ($rel["parentid"] === "") {
                             $rel["parentid"] = null;
                         }
                         $rels[] = $rel;
                     }
                 }
                 try {
                     $res = PersonRelations::syncRelations($p->guid, $this->_user->id, $rels);
                 } catch (Exception $ex) {
                     $res = $ex->getMessage();
                 }
                 if (is_string($res)) {
                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                     $this->_extError = $res;
                     return $p;
                 }
             }
         }
     }
     $this->_error = RestErrorEnum::RE_OK;
     return $person;
 }
Пример #3
0
 /**
  * implementation of abstract parse() operation from RestXMLParser.
  * Notes:
  * - Simple application properties are set in the application model directly 
  * from the XML
  * - Simple collections of associations of the appliation to various other 
  * entities such as middlewares, discipline, etc. are synchronized using 
  * syncDBCollection(), by putting information from the XML into arrays 
  * (hash-tables) and then uniformly passing info from the array to models
  * - Complicated collections of associations such as application contact 
  * metadata and publication have dedicated functions, yet the XML -> array 
  * -> Model logic still stands
  *
  * @xml SimpleXMLElement the root element of the application XML representation
  * 
  * @return Default_Model_Application
  * @access public
  */
 public function parse($xml)
 {
     global $application;
     if (!is_null($this->_user)) {
         $app = new Default_Model_Application();
         try {
             $xml = new SimpleXMLElement($xml);
         } catch (Exception $e) {
             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
             return $app;
         }
         $this->_xml = $xml;
         // basic properties
         $xmli = $xml->xpath('//application:application');
         if (count($xmli) === 0) {
             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
             return $app;
         }
         $xml = $xmli[0];
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             if ($xml->attributes()->id) {
                 $app->id = strval($xml->attributes()->id);
                 $db = $application->getBootstrap()->getResource('db');
                 $db->setFetchMode(Zend_Db::FETCH_OBJ);
                 $r = $db->query('SELECT guid FROM applications WHERE id = ' . $app->id)->fetchAll();
                 if (count($r) > 0) {
                     $app->guid = $r[0]->guid;
                 }
             } else {
                 $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
                 return $app;
             }
         }
         $sync = false;
         if ($this->_parent->getParam('sync') === "true") {
             if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
                 if ($xml->attributes()->id) {
                     $id = strval($xml->attributes()->id);
                     $apps = new Default_Model_Applications();
                     $apps->filter->id->equals($id);
                     if (count($apps->items) > 0) {
                         if ($this->_parent->canSync($apps->items[0])) {
                             $sync = true;
                         }
                     }
                 }
             } else {
                 if ($this->_parent->canSync(null)) {
                     $sync = true;
                 }
             }
         }
         if ($xml->attributes()->tool) {
             $app->tool = strval($xml->attributes()->tool) === "true" ? true : false;
         }
         if (!$sync) {
             if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                 $app->addedBy = $this->_parent->getUser()->id;
             }
         } else {
             $addedby = $this->el($xml, "application:addedby");
             if ($addedby && $addedby->attributes()->id) {
                 $app->addedBy = strval($addedby->attributes()->id);
             } else {
                 $app->addedBy = $this->_parent->getUser()->id;
             }
         }
         $owner = $this->el($xml, "application:owner");
         $ownerID = '';
         // IF NOT SYNCING:
         // if an owner is specified, and either the user has the permission
         // to set it or the user is a service adding a new application, then
         // do set the owner, or else set the owner to be the same user as the
         // one that makes the request if this is a new application, otherwise
         // let it be
         // IF SYNCING:
         // set the owner as if specified. if not, set it to be the same as
         // the addedby property
         if (!$sync) {
             if (!is_null($owner) && !is_null($owner->attributes()->id)) {
                 if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                     if ($this->_user->accountType == 1 || $this->_parent->userIsAdmin()) {
                         $ownerID = strval($owner->attributes()->id);
                     } else {
                         $ownerID = $app->addedBy;
                     }
                 } elseif ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
                     if ($this->_user->privs->canGrantOwnership($app)) {
                         $ownerID = strval($owner->attributes()->id);
                     }
                 }
             } else {
                 if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                     $ownerID = $app->addedBy;
                 }
             }
         } else {
             if (!is_null($owner) && !is_null($owner->attributes()->id)) {
                 $ownerID = strval($owner->attributes()->id);
             } else {
                 if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                     $ownerID = $app->addedBy;
                 }
             }
         }
         if ($ownerID != '') {
             $app->ownerID = $ownerID;
         }
         if ($xml->attributes()->tagPolicy) {
             if ($this->_parent->userIsAdmin() || $newapp || $this->_parent->getUser()->id === $app->addedBy || $this->_parent->getUser()->id === $app->ownerID) {
                 $app->tagPolicy = strval($xml->attributes()->tagPolicy);
             }
         }
         $nameError = '';
         $nameReason = '';
         if ($this->_user->privs->canModifyApplicationName($app)) {
             if (!is_null($this->el($xml, "application:name"))) {
                 if (validateAppName(preg_replace("/-DELETED-.{8}-.{4}-.{4}-.{4}-.{12}/", "", strval($this->el($xml, "application:name"))), $nameError, $nameReason, $this->_parent->getMethod() === RestMethodEnum::RM_POST ? $app->id : null)) {
                     $app->name = strval($this->el($xml, "application:name"));
                 } else {
                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                     $this->_extError = "Invalid application name. " . strip_tags($nameReason);
                     return $app;
                 }
             }
         }
         if ($this->_user->privs->canModifyApplicationDescription($app)) {
             $app->description = strval($this->el($xml, "application:description"));
         }
         if ($this->_user->privs->canModifyApplicationAbstract($app)) {
             $app->abstract = strval($this->el($xml, "application:abstract"));
         }
         if ($this->_user->privs->canModifyApplicationStatus($app)) {
             $status = $this->el($xml, "application:status");
             if ($status && $status->attributes()->id) {
                 $app->statusID = strval($status->attributes()->id);
             }
         }
         if (!$sync) {
             if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                 $app->dateAdded = date('Y-m-d H:i:s');
             }
         } else {
             $dateAdded = $this->el($xml, "application:addedOn");
             if ($dateAdded) {
                 $app->dateAdded = strval($dateAdded);
             } else {
                 if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT) {
                     $app->dateAdded = date('Y-m-d H:i:s');
                 }
             }
         }
         if (!$sync) {
             $app->lastUpdated = date('Y-m-d H:i:s');
         } else {
             $lastUpdated = $this->el($xml, "application:lastUpdated");
             if ($lastUpdated) {
                 $app->lastUpdated = strval($lastUpdated);
             } else {
                 $app->lastUpdated = date('Y-m-d H:i:s');
             }
         }
         if ($this->_parent->userIsAdmin()) {
             if ($sync) {
                 if ($xml->attributes()->moderated) {
                     $app->moderated = strval($xml->attributes()->moderated);
                 }
                 if ($app->moderated) {
                     $app->modInfo->moddedBy = strval($this->el($xml, "application:moderator")->attributes()->id);
                     $app->modInfo->moddedOn = $xml->moderatedOn;
                     $app->modInfo->modReason = $xml->moderationReason;
                 }
             }
         }
         if ($this->_parent->userIsAdmin() || $this->_user->privs->canDeleteApplication($app)) {
             if ($sync) {
                 if ($xml->attributes()->deleted) {
                     $app->deleted = strval($xml->attributes()->deleted);
                 }
                 if ($app->deleted) {
                     $app->delInfo->deletedBy = strval($this->el($xml, "application:deleter")->attributes()->id);
                     $app->delInfo->deletedOn = $xml->deletedOn;
                 }
             }
         }
         // also set extended attributes, like deleted, moderated, tags,
         // etc. when syncing, which would normaly be ignored since they are
         // set via separate resources
         //            if ( $sync ) {
         //                TODO: add extra sync code
         //            }
         if ($this->_user->privs->canModifyApplicationLogo($app)) {
             $logo = $this->el($xml, "application:logo");
             $removeLogoCache = false;
             if (!is_null($logo)) {
                 if ($logo->attributes(RestAPIHelper::XMLNS_XSI())->nil === "true") {
                     $app->clearLogo();
                     $removeLogoCache = true;
                 } else {
                     if ($logo->attributes()->type && strval($logo->attributes()->type) === "base64") {
                         // logo is given as byte64 encoded string
                         if (strval($logo) != '') {
                             $app->logo = pg_escape_bytea(strval($logo));
                             $removeLogoCache = true;
                         }
                     } else {
                         // logo is given as URL
                         if (parse_url(strval($logo), PHP_URL_SCHEME) == '') {
                             // no URL scheme present; assume uploaded file though
                             // portal's uploadlogo action in AppsController
                             if (strval($logo) != '') {
                                 try {
                                     $app->logo = pg_escape_bytea(base64_encode(file_get_contents(APPLICATION_PATH . "/../public/" . strval($logo))));
                                     $removeLogoCache = true;
                                 } catch (Exception $e) {
                                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                                     $this->_extError = $e->getMessage();
                                     return $app;
                                 }
                             }
                         } else {
                             // URL scheme present; assume remote file
                             if (strval($logo) != '') {
                                 try {
                                     $app->logo = pg_escape_bytea(base64_encode(file_get_contents(strval($logo))));
                                     $removeLogoCache = true;
                                 } catch (Exception $e) {
                                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                                     $this->_extError = $e->getMessage();
                                     return $app;
                                 }
                             }
                         }
                     }
                 }
             }
             if ($removeLogoCache === true) {
                 $logocachename = APPLICATION_PATH . "/../cache/app-logo-" . $app->id . ".png";
                 if ($app->id != '' && file_exists($logocachename)) {
                     // invalidate logo cache
                     unlink($logocachename);
                     // re-build logo cache
                     $flogo = fopen($logocachename, "w");
                     fwrite($flogo, base64_decode(pg_unescape_bytea($app->logo)));
                     fclose($flogo);
                     $logocachename2 = str_replace("/app-logo", "/55x55/app-logo", $logocachename);
                     $logocachename3 = str_replace("/app-logo", "/100x100/app-logo", $logocachename);
                     $logocachename2 = str_replace(".png", ".jpg", $logocachename2);
                     $logocachename3 = str_replace(".png", ".jpg", $logocachename3);
                     `convert -background white -flatten -strip -interlace Plane -quality 80 -scale 55x55   {$logocachename} {$logocachename2}`;
                     `convert -background white -flatten -strip -interlace Plane -quality 80 -scale 100x100 {$logocachename} {$logocachename3}`;
                 }
             }
         }
         //Set metatype of application (0: software 1: vappliance 2:swappliance )
         if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT || $this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             if (is_numeric(strval($xml->attributes()->metatype)) && intval(strval($xml->attributes()->metatype)) >= 0 && intval(strval($xml->attributes()->metatype)) < 3) {
                 $app->metatype = intval(strval($xml->attributes()->metatype));
             } else {
                 $app->metatype = 0;
             }
         }
         if ($this->_parent->getMethod() === RestMethodEnum::RM_PUT && (count($xml->xpath("//application:category")) == 0 || count($xml->xpath("//discipline:discipline")) == 0 || $app->abstract == '' || $app->description == '')) {
             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
             $this->_extError = "One ore more required entities are missing or contain no data.";
             return $app;
         } else {
             $app->save();
         }
         // handle tags separetely through dedicated resource classes
         // thus avoiding re-implementing complicated permission checks
         if (count($xml->xpath('//application:tag[@xsi:nil="true"]')) != 0) {
             // nil element specified; remove all tags
             try {
                 $taglist = new RestAppTagList(array('id' => $app->id));
                 $xml2 = new SimpleXMLElement(strval($taglist->get()));
                 foreach ($xml2->xpath('//application:tag') as $tag) {
                     $t = new RestAppTagItem(array('tid' => strval($t->attributes()->id)), $taglist);
                     $t->delete();
                 }
             } catch (Exception $e) {
                 $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                 $this->_extError = $e->getMessage();
                 return $app;
             }
         } elseif (count($xml->xpath('//application:tag')) > 0) {
             // non-nil element specified; synchronize tags
             try {
                 //add new
                 $newtags = array();
                 foreach ($xml->xpath('//application:tag') as $tag) {
                     if (strval($tag->attributes()->system) !== "true") {
                         $tagval = $tag->asXML();
                         // TODO: formalize the appdb:appdb head in a constant
                         // and search for other places in the API lib where this should
                         // be done as well
                         $tagval = '<appdb:appdb xmlns:appdb="http://appdb.egi.eu/api/' . $this->_parent->getParam('version') . '/appdb" xmlns:application="http://appdb.egi.eu/api/' . $this->_parent->getParam('version') . '/application">' . $tagval . '</appdb:appdb>';
                         $taglist = new RestAppTagList(array_merge($this->_parent->getParams(), array('id' => $app->id, 'data' => $tagval)));
                         $tagres = RestAPIHelper::wrapResponse(strval($taglist->get()));
                         $tagxml = new SimpleXMLElement(strval($tagres));
                         $tagxml = $tagxml->xpath("//application:tag");
                         $found = false;
                         $tval = new SimpleXMLElement($tagval);
                         $tval = $tval->xpath("//application:tag");
                         $tval = strval($tval[0]);
                         foreach ($tagxml as $tt) {
                             if (strval($tt) === $tval) {
                                 $found = true;
                                 break;
                             }
                         }
                         if (!$found) {
                             $taglist->put();
                         }
                         $newtags[] = strval($tag);
                     }
                 }
                 //remove non-existent
                 $taglist = new RestAppTagList(array('id' => $app->id));
                 $tagsxml = strval($taglist->get());
                 $tagsxml = '<appdb:appdb xmlns:appdb="http://appdb.egi.eu/api/' . $this->_parent->getParam('version') . '/appdb" xmlns:application="http://appdb.egi.eu/api/' . $this->_parent->getParam('version') . '/application">' . $tagsxml . '</appdb:appdb>';
                 $xml2 = new SimpleXMLElement($tagsxml);
                 foreach ($xml2->xpath('//application:tag[not(@system="true")]') as $tag) {
                     if (!in_array(strval($tag), $newtags)) {
                         $t = new RestAppTagItem(array_merge($this->_parent->getParams(), array('tid' => strval($t->attributes()->id))), $taglist);
                         $t->delete();
                     }
                 }
             } catch (Exception $e) {
                 $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                 $this->_extError = $e->getMessage();
                 return $app;
             }
         }
         // if no application:tag element specified, then ignore any existing tags (insert xor partial update)
         if ($this->_user->privs->canModifyApplicationCategory($app)) {
             $data = $this->buildCollection($xml, "//application:category", "categoryID");
             $this->syncDBCollection("appid", $app->id, "categoryid", "AppCategories", "AppCategory", $data);
         }
         /* set primary category */
         if (count($xml->xpath('//application:category[@primary="true"]')) > 0) {
             $catid = $xml->xpath('//application:category[@primary="true"]');
             $catid = strval($catid[0]->attributes()->id);
             // This is MUCH faster than using the model
             db()->query("UPDATE appcategories SET isprimary = TRUE WHERE categoryid = ? AND appid = ?", array($catid, $app->id));
             //				$cats = new Default_Model_AppCategories();
             //				$cats->filter->appid->equals($app->id)->and($cats->filter->categoryid->equals($catid));
             //				if ( count($cats->items) > 0 ) {
             //					$cat = $cats->items[0];
             //					$cat->isPrimary = true;
             //					$cat->save();
             //				}
         }
         /* */
         if ($this->_user->privs->canModifyApplicationLanguage($app)) {
             $data = $this->buildCollection($xml, "//application:language", "proglangID");
             $this->syncDBCollection("appid", $app->id, "proglangid", "AppProgLangs", "AppProgLang", $data);
         }
         if ($this->_user->privs->canModifyApplicationDiscipline($app)) {
             $data = $this->buildCollection($xml, "//discipline:discipline", "disciplineID");
             $this->syncDBCollection("appid", $app->id, "disciplineid", "AppDisciplines", "AppDiscipline", $data);
         }
         if ($this->_user->privs->canModifyApplicationVO($app) && trim($app->metatype) != "2") {
             //Do not allow editing for software appliances
             $data = $this->buildCollection($xml, "//vo:vo", "vo");
             $this->syncDBCollection("appid", $app->id, "void", "AppVOs", "AppVO", $data, "vo");
         }
         if ($this->_user->privs->canModifyApplicationMiddleware($app)) {
             $data = $this->buildCollection($xml, "//middleware:middleware", "mw");
             $this->syncDBCollection("appid", $app->id, "middlewareid", "AppMiddlewares", "AppMiddleware", $data, "mw");
         }
         if ($this->_parent->method === RestMethodEnum::RM_POST && $this->_user->privs->canAssociatePersonToApplication($app)) {
             $data = $this->buildCollection($xml, "//application:contact", "scicon");
             $this->syncDBCollection("appid", $app->id, "researcherid", "ResearchersApps", "ResearchersApp", $data, "scicon");
             $data = null;
             $i = 0;
             $xmli = $xml->xpath("//application:contact/application:contactItem");
             if (count($xmli) != 0) {
                 $data = array();
                 foreach ($xmli as $x) {
                     if ($x->attributes(RestAPIHelper::XMLNS_XSI())->nil === "true") {
                         $data = array();
                         break;
                     } else {
                         $xp = $x->xpath("parent::*");
                         $data['cntpnt' . $i] = '{"researcherid": "' . strval($xp[0]->attributes()->id) . '", "itemtype": "' . strval($x->attributes()->type) . '", "itemid": "' . strval($x->attributes()->id) . '", "item": "' . strval($x) . '"}';
                         $i = $i + 1;
                     }
                 }
             }
             if (!is_null($data)) {
                 $this->syncAppContactItems($app->id, $data);
             }
         }
         if ($this->_user->privs->canModifyApplicationCountry($app)) {
             $data = $this->buildCollection($xml, "//regional:country", "countryid");
             $this->syncDBCollection("appid", $app->id, "countryid", "AppManualCountries", "AppManualCountry", $data);
         }
         if ($this->_user->privs->canModifyApplicationURLs($app)) {
             $data = $this->buildCollection($xml, "//application:url", "url");
             $this->syncDBCollection("appid", $app->id, "id", "AppUrls", "AppUrl", $data, "url");
         }
         if ($this->_user->privs->canModifyApplicationLicenses($app)) {
             $data = $this->buildCollection($xml, "//application:license", "license");
             if (is_null($data)) {
                 $data = array();
             }
             $this->syncDBCollection("appid", $app->id, "licenseid", "AppLicenses", "AppLicense", $data, "license");
         }
         if ($this->_user->privs->canModifyApplicationDocuments($app)) {
             $xmli = $xml->xpath("publication:publication");
             $docdata = null;
             if (count($xmli) != 0) {
                 $docdata = array();
                 foreach ($xmli as $x) {
                     if (strval($x->attributes(RestAPIHelper::XMLNS_XSI())->nil) === "true") {
                         $docdata = array();
                         break;
                     }
                     $docdatum = array();
                     $docdatum['id'] = strval($x->attributes()->id);
                     $this->pubprop($x, "title", $docdatum);
                     $this->pubprop($x, "url", $docdatum);
                     $this->pubprop($x, "conference", $docdatum);
                     $this->pubprop($x, "proceedings", $docdatum);
                     $this->pubprop($x, "journal", $docdatum);
                     $this->pubprop($x, "isbn", $docdatum);
                     $this->pubprop($x, "volume", $docdatum);
                     $this->pubprop($x, "startPage", $docdatum);
                     $this->pubprop($x, "endPage", $docdatum);
                     $this->pubprop($x, "year", $docdatum);
                     $this->pubprop($x, "publisher", $docdatum);
                     $type = $this->el($x, "publication:type");
                     if (!is_null($type)) {
                         $docdatum['typeID'] = strval($type->attributes()->id);
                     }
                     $intAuthors = array();
                     $extAuthors = array();
                     $authors = $x->xpath('publication:author[@type="internal"]');
                     foreach ($authors as $a) {
                         if ($this->el($a, "person:person") === null) {
                             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
                             $this->_extError = "expected 'person:person' element under 'publication:author[@type=\"internal\"]' not found";
                             return $app;
                         } else {
                             $intAuthors[] = array(strval($this->el($a, "person:person")->attributes()->id), strval($a->attributes()->main));
                         }
                     }
                     $docdatum['intAuthors'] = $intAuthors;
                     $authors = $x->xpath('publication:author[@type="external"]');
                     foreach ($authors as $a) {
                         if ($this->el($a, "publication:extAuthor") === null) {
                             $this->_error = RestErrorEnum::RE_INVALID_REPRESENTATION;
                             $this->_extError = "expected 'publication:extAuthor' element under 'publication:author[@type=\"external\"]' not found";
                             return $app;
                         } else {
                             $extAuthors[] = array(strval($this->el($a, "publication:extAuthor")), strval($a->attributes()->main));
                         }
                     }
                     $docdatum['extAuthors'] = $extAuthors;
                     $docdata[] = $docdatum;
                 }
             }
             if (!is_null($docdata)) {
                 $docs = new Default_Model_AppDocuments();
                 $docs->filter->appid->equals($app->id);
                 $docs->refresh();
                 $docCount = count($docs->items);
                 //handle existing and deleted entries
                 for ($i = $docCount - 1; $i >= 0; $i--) {
                     $existing = null;
                     if (is_array($docdata)) {
                         for ($j = 0; $j < count($docdata); $j++) {
                             $doc = $docs->items[$i];
                             if ($doc->id == $docdata[$j]['id']) {
                                 $existing = $this->populateAppDoc($doc, $docdata[$j]);
                                 $docdata[$j]['PARSED'] = true;
                                 break;
                             }
                         }
                     }
                     if ($existing === null) {
                         $docs->remove($docs->items[$i]);
                     } else {
                         $existing->save();
                     }
                 }
                 //handle new entries
                 if (is_array($docdata)) {
                     foreach ($docdata as $docdatum) {
                         if (!isset($docdatum['PARSED']) || $docdatum['PARSED'] !== true) {
                             $doc = new Default_Model_AppDocument();
                             //first time only main data is saved
                             $doc->appID = $app->id;
                             $doc = $this->populateAppDoc($doc, $docdatum);
                             $docs->add($doc);
                             //second time referenced data is saved
                             $doc = $this->populateAppDoc($doc, $docdatum);
                             $doc->save();
                         }
                     }
                 }
             }
         }
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             $xrels = $xml->xpath("application:relation");
             $ps = new Default_Model_Applications();
             $ps->filter->id->equals($app->id);
             $p = null;
             $res = true;
             if (count($ps->items) > 0) {
                 $p = $ps->items[0];
             }
             if ($p !== null) {
                 $rels = array();
                 if (count($xml->xpath('application:relation[@xsi:nil="true"]')) === 0) {
                     foreach ($xrels as $x) {
                         $targuid = trim(strval($x->attributes()->targetguid));
                         $subguid = trim(strval($x->attributes()->subjectguid));
                         $rel = array("id" => trim(strval($x->attributes()->id)), "parentid" => trim(strval($x->attributes()->parentid)));
                         if ($targuid === "") {
                             $rel["subjectguid"] = $subguid;
                         } else {
                             if ($subguid === "") {
                                 $rel["targetguid"] = $targuid;
                             }
                         }
                         if ($rel["parentid"] === "") {
                             $rel["parentid"] = null;
                         }
                         $rels[] = $rel;
                     }
                 }
                 try {
                     $res = ApplicationRelations::syncRelations($p->guid, $this->_user->id, $rels);
                 } catch (Exception $ex) {
                     $res = $ex->getMessage();
                     error_log($res);
                 }
                 if (is_string($res)) {
                     $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                     $this->_extError = $res;
                     return $p;
                 }
             }
         }
         if ($this->_parent->getMethod() === RestMethodEnum::RM_POST) {
             $xrels = $xml->xpath("application:extrelation");
             $ps = new Default_Model_Applications();
             $ps->filter->id->equals($app->id);
             $p = null;
             if (count($ps->items) > 0) {
                 $p = $ps->items[0];
             }
             if ($p !== null && trim($app->metatype) !== "2") {
                 $rels = array();
                 if (count($xml->xpath('application:extrelation[@xsi:nil="true"]')) === 0) {
                     foreach ($xrels as $x) {
                         $targuid = trim(strval($x->attributes()->targetguid));
                         $subguid = trim(strval($x->attributes()->subjectguid));
                         $rel = array("id" => trim(strval($x->attributes()->id)), "hidden" => trim(strval($x->attributes()->hidden)));
                         $rels[] = $rel;
                     }
                     try {
                         $res = ApplicationRelations::hideExternalRelations($p->guid, $this->_user->id, $rels);
                     } catch (Exception $ex) {
                         $res = $ex->getMessage();
                         error_log($res);
                     }
                     if (is_string($res)) {
                         $this->_error = RestErrorEnum::RE_BACKEND_ERROR;
                         $this->_extError = $res;
                         return $p;
                     }
                 }
             }
         }
     }
     $this->_error = RestErrorEnum::RE_OK;
     return $app;
 }