diff --git a/src/Pelagos/Bundle/AppBundle/Command/BackFillApprovedDateDifCommand.php b/src/Pelagos/Bundle/AppBundle/Command/BackFillApprovedDateDifCommand.php index a128bde9d0..2bc282ded6 100644 --- a/src/Pelagos/Bundle/AppBundle/Command/BackFillApprovedDateDifCommand.php +++ b/src/Pelagos/Bundle/AppBundle/Command/BackFillApprovedDateDifCommand.php @@ -69,16 +69,15 @@ protected function execute(InputInterface $input, OutputInterface $output) $newRevision->getRev() ); - if ($articleDiff['status']['same'] === DIF::STATUS_APPROVED) { + if ($i === ($numberOfRevisions - 1) and $articleDiff['status']['same'] === DIF::STATUS_APPROVED) { $approvedDateTimeStamp = $articleDiff['modificationTimeStamp']['old']; - break; - } else if ($articleDiff['status']['new'] === DIF::STATUS_APPROVED) { + } + + if ($articleDiff['status']['new'] === DIF::STATUS_APPROVED) { if (!empty($articleDiff['modificationTimeStamp']['new'])) { $approvedDateTimeStamp = $articleDiff['modificationTimeStamp']['new']; - break; } else { $approvedDateTimeStamp = $articleDiff['modificationTimeStamp']['same']; - break; } } } diff --git a/src/Pelagos/Bundle/AppBundle/Controller/Api/DIFController.php b/src/Pelagos/Bundle/AppBundle/Controller/Api/DIFController.php index 055d91eb48..dab2bdf06a 100644 --- a/src/Pelagos/Bundle/AppBundle/Controller/Api/DIFController.php +++ b/src/Pelagos/Bundle/AppBundle/Controller/Api/DIFController.php @@ -7,7 +7,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use FOS\RestBundle\Controller\Annotations as Rest; @@ -206,7 +206,7 @@ public function patchAction($id, Request $request) * * @param integer $id The id of the DIF to submit. * - * @throws AccessDeniedException When the DIF authenticated user does not have permission to submit the DIF. + * @throws AccessDeniedHttpException When the DIF authenticated user does not have permission to submit the DIF. * @throws BadRequestHttpException When the DIF could not be submitted. * * @ApiDoc( @@ -229,7 +229,7 @@ public function submitAction($id) // Check if the user has permission to submit it. if (!$this->isGranted(DIFVoter::CAN_SUBMIT, $dif)) { // Throw an exception if they don't. - throw new AccessDeniedException( + throw new AccessDeniedHttpException( 'You do not have sufficient privileges to submit this ' . $dif::FRIENDLY_NAME . '.' ); } @@ -253,7 +253,7 @@ public function submitAction($id) * * @param integer $id The id of the DIF to approve. * - * @throws AccessDeniedException When the DIF authenticated user does not have permission to approve the DIF. + * @throws AccessDeniedHttpException When the DIF authenticated user does not have permission to approve the DIF. * @throws BadRequestHttpException When the DIF could not be approved. * * @ApiDoc( @@ -276,7 +276,7 @@ public function approveAction($id) // Check if the user has permission to approve it. if (!$this->isGranted(DIFVoter::CAN_APPROVE, $dif)) { // Throw an exception if they don't. - throw new AccessDeniedException( + throw new AccessDeniedHttpException( 'You do not have sufficient privileges to approve this ' . $dif::FRIENDLY_NAME . '.' ); } @@ -304,7 +304,7 @@ public function approveAction($id) * * @param integer $id The id of the DIF to reject. * - * @throws AccessDeniedException When the DIF authenticated user does not have permission to reject the DIF. + * @throws AccessDeniedHttpException When the DIF authenticated user does not have permission to reject the DIF. * @throws BadRequestHttpException When the DIF could not be rejected. * * @ApiDoc( @@ -327,7 +327,7 @@ public function rejectAction($id) // Check if the user has permission to reject it. if (!$this->isGranted(DIFVoter::CAN_REJECT, $dif)) { // Throw an exception if they don't. - throw new AccessDeniedException( + throw new AccessDeniedHttpException( 'You do not have sufficient privileges to reject this ' . $dif::FRIENDLY_NAME . '.' ); } @@ -351,7 +351,7 @@ public function rejectAction($id) * * @param integer $id The id of the DIF to unlock. * - * @throws AccessDeniedException When the DIF authenticated user does not have permission to unlock the DIF. + * @throws AccessDeniedHttpException When the DIF authenticated user does not have permission to unlock the DIF. * @throws BadRequestHttpException When the DIF could not be unlocked. * * @ApiDoc( @@ -374,7 +374,7 @@ public function unlockAction($id) // Check if the user has permission to unlock it. if (!$this->isGranted(DIFVoter::CAN_UNLOCK, $dif)) { // Throw an exception if they don't. - throw new AccessDeniedException( + throw new AccessDeniedHttpException( 'You do not have sufficient privileges to unlock this ' . $dif::FRIENDLY_NAME . '.' ); } @@ -398,7 +398,7 @@ public function unlockAction($id) * * @param integer $id The id of the DIF to request unlock for. * - * @throws AccessDeniedException When the authenticated user does not have + * @throws AccessDeniedHttpException When the authenticated user does not have * permission to request unlock for the DIF. * @throws BadRequestHttpException When the DIF could not be requested to be unlocked. * @@ -423,7 +423,7 @@ public function requestUnlockAction($id) // Check if the user has permission to request it be unlocked. if (!$this->isGranted(DIFVoter::CAN_REQUEST_UNLOCK, $dif)) { // Throw an exception if they don't. - throw new AccessDeniedException( + throw new AccessDeniedHttpException( 'You do not have sufficient privileges to request this ' . $dif::FRIENDLY_NAME . ' be unlocked' ); } diff --git a/src/Pelagos/Bundle/AppBundle/Controller/UI/DatasetDownloadReportController.php b/src/Pelagos/Bundle/AppBundle/Controller/UI/DatasetDownloadReportController.php index b79f877802..6aa73df169 100644 --- a/src/Pelagos/Bundle/AppBundle/Controller/UI/DatasetDownloadReportController.php +++ b/src/Pelagos/Bundle/AppBundle/Controller/UI/DatasetDownloadReportController.php @@ -4,15 +4,17 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; - use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Pelagos\Bundle\AppBundle\Form\ReportDatasetDownloadType; + +use Pelagos\Exception\InvalidDateSelectedException; + use Pelagos\Entity\Dataset; use Pelagos\Entity\LogActionItem; use Pelagos\Entity\Person; -use Pelagos\Exception\InvalidDateSelectedException; +use Pelagos\Entity\DatasetSubmission; /** * The dataset download report generator. @@ -99,7 +101,8 @@ protected function getData(array $options = null) 'PRIMARY POINT OF CONTACT EMAIL', 'TOTAL DOWNLOADS', '# OF GOMRI DOWNLOADS', - '# OF GOOGLE DOWNLOADS' + '# OF GOOGLE DOWNLOADS', + 'FILE SIZE(MB)' )); //prepare body's data @@ -141,6 +144,7 @@ protected function getData(array $options = null) 'totalCount' => 0, 'GoMRI' => 0, 'NonGoMRI' => 0, + 'fileSize' => null ); $dataset = $this->container->get('doctrine')->getRepository(Dataset::class) @@ -160,6 +164,12 @@ protected function getData(array $options = null) ->getEmailAddress(); } + // get file size from dataset submission + $datasetSubmission = $dataset->getDatasetSubmission(); + if ($datasetSubmission instanceof DatasetSubmission) { + $dataArray[$currentIndex]['fileSize'] = $this->formatSizeUnits($datasetSubmission->getDatasetFileSize()); + } + } //count user downloads and total download if ($result['payLoad']['userType'] == 'GoMRI') { @@ -173,4 +183,21 @@ protected function getData(array $options = null) } return array_merge($this->getDefaultHeaders(), $additionalHeaders, $labels, $dataArray); } + + /** + * Used to format the file size units to MB. + * + * @param integer $fileSizeBytes File size in bytes. + * + * @return float + */ + private function formatSizeUnits($fileSizeBytes) + { + if ($fileSizeBytes) { + // Formats the size to MB + $fileSizeBytes = number_format(($fileSizeBytes / 1000000), 6); + } + + return $fileSizeBytes; + } } diff --git a/src/Pelagos/Bundle/AppBundle/Controller/UI/SideBySideController.php b/src/Pelagos/Bundle/AppBundle/Controller/UI/SideBySideController.php new file mode 100644 index 0000000000..a63574f5c9 --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Controller/UI/SideBySideController.php @@ -0,0 +1,206 @@ + 'Unsubmitted', + DatasetSubmission::STATUS_INCOMPLETE => 'Draft', + DatasetSubmission::STATUS_COMPLETE => 'Submitted', + DatasetSubmission::STATUS_IN_REVIEW => 'In Review', + ); + + /** + * The default action for Side by Side. + * + * @param Request $request The Symfony request object. + * @param string|null $udi The UDI of the Dataset to load. + * + * @Route("/{udi}") + * + * @return Response A Response instance. + */ + public function defaultAction(Request $request, $udi = null) + { + if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) { + return $this->redirect('/user/login?destination=' . $request->getPathInfo()); + } + + if (!$this->isGranted(array('ROLE_DATA_REPOSITORY_MANAGER', 'ROLE_SUBJECT_MATTER_EXPERT'))) { + return $this->render('PelagosAppBundle:template:AdminOnly.html.twig'); + } + + return $this->render( + 'PelagosAppBundle:SideBySide:index.html.twig' + ); + } + + /** + * The get versions action for Side by Side. + * + * @param Request $request The Symfony request object. + * + * @Route("/") + * + * @Method("POST") + * + * @return Response A Response instance. + */ + public function getVersions(Request $request) + { + $udi = $request->request->get('udi'); + + try { + $datasetSubmissionHistory = $this->getDatasetSubmissionHistory($udi); + } catch (\Exception $e) { + return new JsonResponse( + null, + JsonResponse::HTTP_BAD_REQUEST + ); + } + + $submissions = array(); + + foreach ($datasetSubmissionHistory->getIterator() as $i => $submission) { + $data = array(); + $data['version'] = $i; + $data['udi'] = $submission->getDataset()->getUdi(); + $data['sequence'] = $submission->getSequence(); + $data['status'] = self::SUBMISSIONS_STATES[$submission->getStatus()]; + $data['modifier'] = $submission->getModifier()->getLastName() . + ', ' . $submission->getModifier()->getFirstName(); + $data['modificationtimestamp'] = $submission->getModificationTimeStamp()->format('c'); + $submissions[] = $data; + } + + return new JsonResponse( + $submissions, + JsonResponse::HTTP_OK + ); + } + + /** + * The get submission form action for the Side By Side controller. + * + * @param Request $request The Symfony request object. + * @param string|null $udi The UDI of the Dataset to load. + * @param string|null $revision The revision number of the Submission to load. + * + * @throws \Exception If revision does not exists. + * + * @Route("/getForm/{udi}/{revision}") + * + * @return Response A Response instance. + */ + public function getSubmissionFormAction(Request $request, $udi = null, $revision = null) + { + if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) { + return $this->redirect('/user/login?destination=' . $request->getPathInfo()); + } + + try { + $datasetSubmissionHistory = $this->getDatasetSubmissionHistory($udi); + + if ($datasetSubmissionHistory->count() < $revision and $revision !== null) { + throw new \Exception("Revision $revision does not exist for UDI: $udi"); + } + } catch (\Exception $e) { + return new TerminateResponse( + $e->getMessage(), + Response::HTTP_BAD_REQUEST + ); + } + + if ($revision !== null) { + $datasetSubmission = $datasetSubmissionHistory[$revision]; + } else { + $datasetSubmission = $datasetSubmissionHistory->first(); + } + + $researchGroupList = array(); + $account = $this->getUser(); + if (null !== $account) { + $user = $account->getPerson(); + // Find all RG's user has CREATE_DIF_DIF_ON on. + $researchGroups = $user->getResearchGroups(); + $researchGroupList = array_map( + function ($researchGroup) { + return $researchGroup->getId(); + }, + $researchGroups + ); + } + + $form = $this->get('form.factory')->createNamed(null, DatasetSubmissionType::class, $datasetSubmission); + + $terminateResponse = new TerminateResponse(); + + return $this->render( + 'PelagosAppBundle:SideBySide:submissionForm.html.twig', + array( + 'form' => $form->createView(), + 'datasetSubmission' => $datasetSubmission, + 'showForceImport' => false, + 'showForceDownload' => false, + 'researchGroupList' => $researchGroupList, + 'mode' => 'view', + ), + $terminateResponse + ); + } + + /** + * Get the dataset submission history from UDI. + * + * @param string|null $udi The UDI of the Dataset to load. + * + * @throws \Exception If dataset if not found. + * @throws \Exception If more than one dataset is returned. + * + * @return DatasetSubmissionHistory An array collection of submissions. + */ + private function getDatasetSubmissionHistory($udi) + { + $datasets = $this->entityHandler->getBy(Dataset::class, array('udi' => $udi)); + + if (count($datasets) == 0) { + throw new \Exception("No dataset found for UDI: $udi"); + } + + if (count($datasets) > 1) { + throw new \Exception("Got more than one return for UDI: $udi"); + } + + $dataset = $datasets[0]; + + return $dataset->getDatasetSubmissionHistory(); + } +} diff --git a/src/Pelagos/Bundle/AppBundle/Form/DIFType.php b/src/Pelagos/Bundle/AppBundle/Form/DIFType.php index 719f23506d..f78d679a20 100644 --- a/src/Pelagos/Bundle/AppBundle/Form/DIFType.php +++ b/src/Pelagos/Bundle/AppBundle/Form/DIFType.php @@ -191,7 +191,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'widget' => 'single_text', 'html5' => false, 'format' => 'yyyy-MM-dd', - 'required' => false, + 'required' => true, 'attr' => array( 'placeholder' => 'yyyy-mm-dd' ), @@ -202,7 +202,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) 'widget' => 'single_text', 'html5' => false, 'format' => 'yyyy-MM-dd', - 'required' => false, + 'required' => true, 'attr' => array( 'placeholder' => 'yyyy-mm-dd' ), diff --git a/src/Pelagos/Bundle/AppBundle/Resources/config/routing_ui.yml b/src/Pelagos/Bundle/AppBundle/Resources/config/routing_ui.yml index f55738d6d6..35f28da8ac 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/config/routing_ui.yml +++ b/src/Pelagos/Bundle/AppBundle/Resources/config/routing_ui.yml @@ -74,4 +74,8 @@ pelagos_end_review_ui: resource: Pelagos\Bundle\AppBundle\Controller\UI\EndReviewController pelagos_national_data_center_ui: - resource: Pelagos\Bundle\AppBundle\Controller\UI\NationalDataCenterController \ No newline at end of file + resource: Pelagos\Bundle\AppBundle\Controller\UI\NationalDataCenterController + +pelagos_side_by_side_ui: + resource: Pelagos\Bundle\AppBundle\Controller\UI\SideBySideController + \ No newline at end of file diff --git a/src/Pelagos/Bundle/AppBundle/Resources/public/css/dataset-submission.css b/src/Pelagos/Bundle/AppBundle/Resources/public/css/dataset-submission.css index 1887d2b3a8..fdacac0f51 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/public/css/dataset-submission.css +++ b/src/Pelagos/Bundle/AppBundle/Resources/public/css/dataset-submission.css @@ -27,6 +27,7 @@ .keywordinput { width:350px; + height:100%; } .dstablink { @@ -170,3 +171,5 @@ table.dataTable.stripe tbody tr:hover { { display:none; } + + diff --git a/src/Pelagos/Bundle/AppBundle/Resources/public/css/sidebyside.css b/src/Pelagos/Bundle/AppBundle/Resources/public/css/sidebyside.css new file mode 100644 index 0000000000..2c331ac69a --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/public/css/sidebyside.css @@ -0,0 +1,76 @@ +.spinner { + display:none; +} + +.side-table { + width: 100%; +} + +.version-select { + height:25px; +} + +.grid-container { + display:grid; + grid-template-columns: repeat(2, 1fr); + align-items: start; + overflow:auto; + height:100%; +} + +.grid-group { + display:contents; + grid-auto-rows: 1fr; +} + +#left.grid-group > div.ds-metadata { + grid-column: 1; +} + +#right.grid-group > div.ds-metadata { + grid-column: 2; +} + +.grid-group > div.ds-metadata:nth-child(1) { + grid-row: 1; + +} + +.grid-group > div.ds-metadata:nth-child(2) { + grid-row: 2; +} + +.grid-group > div.ds-metadata:nth-child(3) { + grid-row: 3; + grid-column: 1; +} + +.grid-group > div.ds-metadata:nth-child(4) { + grid-row: 4; + grid-column: 1; +} + +.grid-group > div.ds-metadata:nth-child(5) { + grid-row: 5; + grid-column: 1; +} + +.grid-group > div.ds-metadata:nth-child(6) { + grid-row: 6; + grid-column: 1; +} + +.grid-group > div.ds-metadata:nth-child(7) { + grid-row: 7; + grid-column: 1; +} + +.grid-group > div.ds-metadata:nth-child(8) { + grid-row: 8; + grid-column: 1; +} + + + + + diff --git a/src/Pelagos/Bundle/AppBundle/Resources/public/js/dif.js b/src/Pelagos/Bundle/AppBundle/Resources/public/js/dif.js index 0ac2a1f276..4d51111142 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/public/js/dif.js +++ b/src/Pelagos/Bundle/AppBundle/Resources/public/js/dif.js @@ -148,14 +148,20 @@ $(document).ready(function() jQuery.validator.addMethod("trueISODate", function(value, element) { var regPattern = /^\d{4}-\d{1,2}-\d{1,2}$/ return this.optional(element) || ((Date.parse(value)) && regPattern.test(value)); + },function (params, element) { + return "Please enter a valid ISO Date" }); difValidator = $("#difForm").validate({ ignore: ".ignore", messages: { geoloc: "Click on Spatial Wizard Button!", - estimatedStartDate: "Start Date is not a valid ISO date", - estimatedEndDate: "End Date is not a valid ISO date" + estimatedStartDate: { + required: "Start Date is a required field." + }, + estimatedEndDate: { + required: "End Date is a required field." + } }, submitHandler: function(form) { saveDIF(form); @@ -611,11 +617,11 @@ function updateDIF(form) type: "PATCH", datatype: "json", data: formData - }).success(function(json, textStatus, jqXHR) { + }).done(function(json, textStatus, jqXHR) { if (jqXHR.status === 204) { response.status = "success"; } - }).error(function (json, text, jqXHR) { + }).fail(function (json, text, jqXHR) { var errorMessage = JSON.parse(json.responseText); response.status = "error"; response.message = errorMessage.message; @@ -660,6 +666,10 @@ function updateDIF(form) "

The application with DIF ID: " + udi + " failed to complete action!" + "

"; } + } else { + var title = "Unable to process DIF form"; + var message = "

There was an error processing your request. Your session might have expired.
" + + "If the problem still persists after you re-login, please contact the administrator.

"; } hideSpinner(); diff --git a/src/Pelagos/Bundle/AppBundle/Resources/public/js/gMap.js b/src/Pelagos/Bundle/AppBundle/Resources/public/js/gMap.js new file mode 100644 index 0000000000..ab3279aab9 --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/public/js/gMap.js @@ -0,0 +1,63 @@ +(function($) { + "use strict"; + + $.fn.gMap = function(options) { + return this.each(function() { + var gml = $(this).data("extent"); + + var thisMap = this; + + jQuery.ajax({ + url: Routing.generate("pelagos_app_gml_towkt"), + type: "POST", + data: {gml: gml}, + context: document.body + }) + .done(function(wkt) { + renderMap(thisMap, wkt); + }); + }); + }; + + function renderMap(map, wkt) { + var raster = new ol.layer.Tile({ + source: new ol.source.OSM() + }); + + var format = new ol.format.WKT(); + + var googleLayerSatellite = new ol.layer.Tile({ + title: "Google Satellite", + source: new ol.source.TileImage({ + url: "https://mt1.google.com/vt/lyrs=s&hl=pl&&x={x}&y={y}&z={z}" + }), + }); + + var feature = format.readFeature(wkt, { + dataProjection: "EPSG:4326", + featureProjection: "EPSG:3857" + }); + + var vector = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [feature] + }) + }); + + var view = new ol.View({ + center: ol.proj.fromLonLat([-90.5, 25]), + zoom: 4, + maxZoom: 12, + minZoom: 1 + }); + + var map = new ol.Map({ + target: map, + layers: [ + googleLayerSatellite, vector + ], + view: view + }); + + }; +}(jQuery)); \ No newline at end of file diff --git a/src/Pelagos/Bundle/AppBundle/Resources/public/js/sidebyside.js b/src/Pelagos/Bundle/AppBundle/Resources/public/js/sidebyside.js new file mode 100644 index 0000000000..b30743ba8f --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/public/js/sidebyside.js @@ -0,0 +1,95 @@ +var $ = jQuery.noConflict(); + +$(document).ready(function() +{ + "use strict"; + + $("#get-versions-button").click(function (){ + var udi = $("input[name=udi]").val().trim(); + jQuery.ajax({ + url: Routing.generate("pelagos_app_ui_sidebyside_getversions", {udi: udi}), + type: "POST", + data: {udi: udi}, + context: document.body + }) + .success(function(data) { + var select = $("select.version-select"); + select.find("option").remove(); + + $.each(data, function(index, item) { + var option = new Option(item.sequence, item.sequence); + $(option).data("udi", item.udi); + $(option).data("modificationtimestamp", item.modificationtimestamp); + $(option).data("status", item.status); + $(option).data("version", item.version); + $(option).data("modifier", item.modifier); + select.append(option); + }); + + $(".right-version").find("select option:selected") + .prop("selected", false) + .next() + .prop("selected", "selected"); + select.change(); + }) + .error(function() { + var n = new noty({ + text: "UDI:" + udi + " not found!", + type: "error", + theme: "relax", + timeout: 3000, + animation: { + open: {opacity: "toggle"}, // fadeIn + close: {opacity: "toggle"}, // fadeOut + } + }); + $("input[name=udi]").val(""); + }); + }); + + $(".left-version").find("select").change(function() { + var version = $(this).find("option:selected").data("version"); + var udi = $(this).find("option:selected").data("udi"); + + $("#left").html($(".spinner div").html()); + $(".udi-title").text(udi); + + $(this).parents("div.left-version") + .find(".submission-status") + .text($(this).find("option:selected").data("status")); + $(this).parents("div.left-version") + .find(".submission-modificationtimestamp") + .text($(this).find("option:selected").data("modificationtimestamp")); + $(this).parents("div.left-version") + .find(".submission-modifier") + .text($(this).find("option:selected").data("modifier")); + var getFormUrl = Routing.generate("pelagos_app_ui_sidebyside_getsubmissionform"); + $("#left").load(getFormUrl + "/" + udi + "/" + version, function() { + $(".smallmap", this).gMap(); + $(".filetabs", this).tabs(); + }); + }); + + $(".right-version").find("select").change(function() { + var version = $(this).find("option:selected").data("version"); + var udi = $(this).find("option:selected").data("udi"); + + $("#right").html($(".spinner div").html()); + $(".udi-title").text(udi); + + $(this).parents("div.right-version") + .find(".submission-status") + .text($(this).find("option:selected").data("status")); + $(this).parents("div.right-version") + .find(".submission-modificationtimestamp") + .text($(this).find("option:selected").data("modificationtimestamp")); + $(this).parents("div.right-version") + .find(".submission-modifier") + .text($(this).find("option:selected").data("modifier")); + var getFormUrl = Routing.generate("pelagos_app_ui_sidebyside_getsubmissionform"); + $("#right").load(getFormUrl + "/" + udi + "/" + version, function() { + $(".smallmap", this).gMap(); + $(".filetabs", this).tabs(); + }); + }); +}); \ No newline at end of file diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/DIF/difForm.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/DIF/difForm.html.twig index afaf86ac07..56b417d89c 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/DIF/difForm.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/DIF/difForm.html.twig @@ -254,16 +254,16 @@

The approximate time period when the data is expected to be collected and generated based on the information provided in your GoMRI proposal.

- -
- Start Date - {{ form_widget(form.estimatedStartDate) }} - to - {{ form_widget(form.estimatedEndDate) }} - End Date - - -
+ +
+ + + Start Date + {{ form_widget(form.estimatedStartDate) }} + to + {{ form_widget(form.estimatedEndDate) }} + End Date +
diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/distribution-contact.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/distribution-contact.html.twig index ffa68aee75..9b97eb29ac 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/distribution-contact.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/distribution-contact.html.twig @@ -3,7 +3,8 @@
- {% set distPoint = form.distributionPoints | first %} + {% set distPoint = form.distributionPoints | first | default %} + {% if (distPoint) %} {% set dataCenterInfo = distPoint.vars.data.dataCenter | default %} {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} @@ -67,6 +68,7 @@ {{ form_row(distPoint.distributionUrl, {attr: {'class' : 'distributionurl'}}) }} + {% endif %}
diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/metadata-contact.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/metadata-contact.html.twig index ab08f8ce1c..cb2f715633 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/metadata-contact.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/metadata-contact.html.twig @@ -1,7 +1,8 @@
Metadata Contact - {% set contact = form.metadataContacts | first %} + {% set contact = form.metadataContacts | first | default %} + {% if (contact) %} + {% endif %}
@@ -41,5 +42,6 @@ {{ form_row(contact.role) }}
diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/submit.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/submit.html.twig index c37c2865b4..1d5bc1e28d 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/submit.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetReview/submit.html.twig @@ -14,12 +14,12 @@ diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetSubmission/submit.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetSubmission/submit.html.twig index 41ec374375..c735518686 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetSubmission/submit.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/DatasetSubmission/submit.html.twig @@ -14,12 +14,12 @@ Thank you for your submission. Please email diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/Default/admin.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/Default/admin.html.twig index ec28066812..520c8505e9 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/Default/admin.html.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/Default/admin.html.twig @@ -9,6 +9,7 @@
  • Dataset Summary and Deletion
  • Dataset Restrictions
  • Dataset Review
  • +
  • Side-by-Side Review
  • End Review Tool
  • Lists diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-submitted.email.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-submitted.email.twig index 5c46fdcaa3..35d0abad96 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-submitted.email.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-submitted.email.twig @@ -1,9 +1,10 @@ {% block subject 'GRIIDC Dataset Submitted' %} {% block body_html %} + {% set submitterGroupsAndRepos = dataset.datasetSubmission.submitter.dataRepositoryNames | merge(dataset.datasetSubmission.submitter.researchGroupNames) %} Dear {{ recipient.firstName }} {{ recipient.lastName }},

    - A dataset has been submitted to the Gulf of Mexico Research Initiative Information and Data Cooperative (GRIIDC) by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} for the dataset {{dataset.udi}}. This dataset has been assigned the doi:{{dataset.doi.doi}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ dataset.datasetSubmission.submitter.researchGroupNames | join(', ') }} and the dataset is associated with {{dataset.researchGroup.name}}.
    + A dataset has been submitted to the Gulf of Mexico Research Initiative Information and Data Cooperative (GRIIDC) by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} for the dataset {{dataset.udi}}. This dataset has been assigned the doi:{{dataset.doi.doi}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ submitterGroupsAndRepos | join (', ') }} and the dataset is associated with {{dataset.researchGroup.name}}.

    Thank you,

    @@ -13,9 +14,10 @@ {% endblock %} {% block body_text %} + {% set submitterGroupsAndRepos = dataset.datasetSubmission.submitter.researchGroupNames | merge(dataset.datasetSubmission.submitter.dataRepositoryNames) %} Dear {{ recipient.firstName }} {{ recipient.lastName }}, -A dataset has been submitted to the Gulf of Mexico Research Initiative Information and Data Cooperative (GRIIDC) by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} for the dataset {{dataset.udi}}. This dataset has been assigned the doi:{{dataset.doi.doi}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ dataset.datasetSubmission.submitter.researchGroupNames | join(', ') }} and the dataset is associated with {{dataset.researchGroup.name}}. +A dataset has been submitted to the Gulf of Mexico Research Initiative Information and Data Cooperative (GRIIDC) by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} for the dataset {{dataset.udi}}. This dataset has been assigned the doi:{{dataset.doi.doi}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ submitterGroupsAndRepos | join (', ') }} and the dataset is associated with {{dataset.researchGroup.name}}. Thank you, diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-updated.email.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-updated.email.twig index 2ca237822a..f1b59cd9e8 100644 --- a/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-updated.email.twig +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/Email/data-managers.dataset-updated.email.twig @@ -1,9 +1,10 @@ {% block subject 'GRIIDC Dataset Submission Updated' %} {% block body_html %} + {% set submitterGroupsAndRepos = dataset.datasetSubmission.submitter.dataRepositoryNames | merge(dataset.datasetSubmission.submitter.researchGroupNames) %} Dear {{ recipient.firstName }} {{ recipient.lastName }},

    - The GRIIDC dataset {{dataset.udi}} has been updated. This update was made by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ dataset.datasetSubmission.submitter.researchGroupNames | join(', ') }} and the dataset is associated with {{dataset.researchGroup.name}}.
    + The GRIIDC dataset {{dataset.udi}} has been updated. This update was made by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ submitterGroupsAndRepos | join (', ') }} and the dataset is associated with {{dataset.researchGroup.name}}.

    Thank you,

    @@ -13,9 +14,10 @@ {% endblock %} {% block body_text %} + {% set submitterGroupsAndRepos = dataset.datasetSubmission.submitter.researchGroupNames | merge(dataset.datasetSubmission.submitter.dataRepositoryNames) %} Dear {{ recipient.firstName }} {{ recipient.lastName }}, -The GRIIDC dataset {{dataset.udi}} has been updated. This update was made by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ dataset.datasetSubmission.submitter.researchGroupNames | join(', ') }} and the dataset is associated with {{dataset.researchGroup.name}}. +The GRIIDC dataset {{dataset.udi}} has been updated. This update was made by {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}}. {{dataset.datasetSubmission.submitter.firstName}} {{dataset.datasetSubmission.submitter.lastName}} is a member of {{ submitterGroupsAndRepos | join (', ') }} and the dataset is associated with {{dataset.researchGroup.name}}. Thank you, diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/extent.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/extent.html.twig new file mode 100644 index 0000000000..93dc159e61 --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/extent.html.twig @@ -0,0 +1,106 @@ +
    + Data Extent +
    + This section collects information describing the spatial and temporal extent + of the dataset contents. If a dataset does not have a relevant location or + time (ex. lab simulations), a short explanation is requested. +
    +
    +
  • - Submission Identifier: + Dataset UDI:

    - - {{ DatasetSubmission.getDatasetSubmissionId }} + + {{ DatasetSubmission.dataset.udi }}

    - Submission Identifier: + Dataset UDI:

    - - {{ DatasetSubmission.getDatasetSubmissionId }} + + {{ DatasetSubmission.dataset.udi }}

    + + + +
    +
    + {{ form_row(form.spatialExtentDescription) }} +
    +
    + +
    +
    +
    + {{ form_row(form.spatialExtent) }} +
    +
    + + + + + + + + + + + + + + + + + + + + +
    + + {% if datasetSubmission.temporalExtentNilReasonType %} + Yes + No + {% else %} + Yes + No + {% endif %} +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    + A description of what the time period represents. If the data represent + real world conditions during the time period, select “ground condition”. + If the data represent a simulation during a specific time period, select + “modeled period”. If the data represent both real world conditions and + model simulations, select “ground condition and modeled period”. If the + data do not have a relevant time period, including mesocosm data, use the + spatial wizard above and select “Non-Spatial”. +
    +
    + {{ form_row(form.temporalExtentDesc) }} +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    + The beginning date when the data were collected. Alternatively, for a model, + the earliest date for which the data were generated. +
    +
    + {{ form_row(form.temporalExtentBeginPosition) }} +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    + The end date when the data were collected. Alternatively, for a model, the + end date for which the data were generated. +
    +
    + {{ form_row(form.temporalExtentEndPosition) }} +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    + 'nilReasoning' the temporal extent means the review will leave the temporal + extent empty because the dataset does not have a relevant temporal extent, + and indicate the reason. +
    +
    + {{ form_row(form.temporalExtentNilReasonType) }} +
    + + diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/file-submit.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/file-submit.html.twig new file mode 100644 index 0000000000..dd5b09f6ac --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/file-submit.html.twig @@ -0,0 +1,140 @@ +{% set sftpuser=app.user.isPosix() %} +
    +
    + Dataset File Transfer Details + + {{ form_widget(form.datasetFileUri, { attr: { class: 'ignore'} }) }} + {{ form_widget(form.datasetFileTransferType) }} + +
    + + +
    + You may upload a single dataset file of any size using direct upload. + If your upload is interrupted, it will attempt to resume automatically. + You may also manually pause your upload and continue later.
    + Note: Even if this window or your web browser has been closed before transfer is complete, + you may return to this page, select the same file, and transfer will resume where it left off. +
    + + + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    +

    Please select from your local machine the data file for the dataset you are submitting.

    +

    Do not include copyrighted materials (e.g. published journal articles) in your data package.

    +
    +
    + + + +
    + {{ form_label(form.datasetFileUri) }} + {{ form.vars.value.datasetFileUri }} + + {{ form_label(form.datasetFileTransferType) }} + {{ form.vars.value.datasetFileTransferType }} +
    +
    +
    + +
    +

    Use this method when you wish to upload the dataset file to GRIIDC (rather than place the dataset file on an HTTP (web) or FTP server and have GRIIDC pull it). + Note: Using this method requires that you have first uploaded the file via SFTP or GridFTP.

    +
    +
    + Your account has been configured for SFTP/GridFTP access. + Click here for instructions.

    +
    +
    +

    + Your account has not been configured for SFTP/GridFTP access. + If you wish to use SFTP/GridFTP, please click here to request SFTP/GridFTP access: +

    +
    +
    +
    + + + + +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    +

    Dataset File Path:

    +

    This is the path on the server to the data file being submitted. Click "Browse..." to find and select the data file you uploaded via SFTP/GridFTP.

    +

    Do not include copyrighted materials (e.g. published journal articles) in your data package.

    +
    +
    +
    + {% if sftpuser %} + {{ form_row(form.datasetFilePath) }} + {% else %} + {{ form_row(form.datasetFilePath, { 'attr':{'disabled':'disabled'} }) }} + {% endif %} +
    + + {% if showForceImport %} +
    + {{ form_widget(form.datasetFileForceImport) }} {{ form_label(form.datasetFileForceImport) }} +
    + {% endif %} +
    +
    +
    +
    + Use this method when you wish to place the dataset file on an HTTP (web) or FTP server at your institution (or elsewhere) and have GRIIDC pull it. +
    + + + + +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    +

    Dataset File URL:

    +

    This is the URL that leads to the data being submitted. Package the components of the datasets (if applicable) to form a single file (e.g. ZIP, TAR).

    +

    Do not include copyrighted materials (e.g. published journal articles) in your data package.

    +
    +
    +
    + {{ form_row(form.datasetFileUrl, { attr: { class: 'ignore'} }) }} +
    +
    +
    +
    + +
    +
    + +
    + {{ form.restrictions.vars.label }} + + + + +
    + + {% image '@PelagosAppBundle/Resources/public/images/info32.png' %} + + {% endimage %} +
    + If data is available to the general public, select "None". Select "Restricted" if data cannot be shared. +
    +
    + {{ form_widget(form.restrictions) }} +
    +
    +
    diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/index.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/index.html.twig new file mode 100644 index 0000000000..b06eb14965 --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/index.html.twig @@ -0,0 +1,159 @@ +{% extends "PelagosAppBundle:template:UI.html.twig" %} + +{% block head %} + {{ parent() }} + + {{ + add_library ( + [ + 'ui.datepicker', + 'ui.dialog', + 'ui.tabs', + 'ui.widget', + ] + ) + }} + + {{ + add_css ( + [ + '//cdn.datatables.net/1.10.7/css/jquery.dataTables.min.css', + '//cdn.datatables.net/select/1.0.1/css/select.dataTables.min.css', + '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css', + '//cdnjs.cloudflare.com/ajax/libs/animate.css/3.3.0/animate.min.css', + '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.11.8/fine-uploader-new.min.css', + '//cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/css/ol.css', + ] + ) + }} + + {% stylesheets + '@PelagosAppBundle/Resources/public/css/dataset-submission.css' + '@PelagosAppBundle/Resources/public/css/fileBrowser.css' + '@PelagosAppBundle/Resources/public/css/dataset-submission-uploader.css' + '@PelagosAppBundle/Resources/public/css/sidebyside.css' + %} + {{ add_css(asset_url) }} + {% endstylesheets %} + + {{ + add_js( + [ + '//cdnjs.cloudflare.com/ajax/libs/jquery-noty/2.3.5/packaged/jquery.noty.packaged.min.js', + '//cdn.datatables.net/1.10.7/js/jquery.dataTables.js', + '//cdn.datatables.net/select/1.0.1/js/dataTables.select.min.js', + '//cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.11.1/jquery.validate.min.js', + '//cdnjs.cloudflare.com/ajax/libs/openlayers/2.13.1/OpenLayers.js', + '//cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.2.0/build/ol.js', + '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js', + '//cdnjs.cloudflare.com/ajax/libs/datejs/1.0/date.min.js', + '//cdnjs.cloudflare.com/ajax/libs/spin.js/2.0.1/spin.min.js', + '//cdnjs.cloudflare.com/ajax/libs/file-uploader/5.11.8/jquery.fine-uploader/jquery.fine-uploader.min.js', + '//maps.google.com/maps/api/js?v=3&key=' ~ google_maps_api_key, + ] + ) + }} + + {% javascripts + '@PelagosAppBundle/Resources/public/js/pelagosNoty.js' + '@FOSJsRoutingBundle/Resources/public/js/router.js' + '@PelagosAppBundle/Resources/public/js/datasetReview.js' + '@PelagosAppBundle/Resources/public/js/sidebyside.js' + '@PelagosAppBundle/Resources/public/js/geoviz.js' + '@PelagosAppBundle/Resources/public/js/mapWizard.js' + '@PelagosAppBundle/Resources/public/js/gMap.js' + '@PelagosAppBundle/Resources/public/js/common.js' + '@PelagosAppBundle/Resources/public/js/fileBrowser.js' + %} + {{ add_js(asset_url) }} + {% endjavascripts %} + + {{ add_js(path('fos_js_routing_js', { callback: 'fos.Router.setData' })) }} +{% endblock %} + +{% block body %} + +
    +

    UDI:

    + + + + + + +
    + + + +
    +
    + + + + + + + + + +
    + + +
    + +
    STATUS
    +
    + +
    MODIFIER
    +
    + +
    TIMESTAMP
    +
    +
    +
    + +
    + + + + + + + + + +
    + + +
    + +
    STATUS
    +
    + +
    MODIFIER
    +
    + +
    TIMESTAMP
    +
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    + {% image '@PelagosAppBundle/Resources/public/images/spinner.gif' %} + + {% endimage %} +
    +
    + +{% endblock %} diff --git a/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/submissionForm.html.twig b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/submissionForm.html.twig new file mode 100644 index 0000000000..687d820300 --- /dev/null +++ b/src/Pelagos/Bundle/AppBundle/Resources/views/SideBySide/submissionForm.html.twig @@ -0,0 +1,33 @@ +{% block body %} +
    + {% include 'PelagosAppBundle:DatasetReview:dataset-contact.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:DatasetReview:dataset-information.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:DatasetReview:keywords.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:SideBySide:extent.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:DatasetReview:distribution-info.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:DatasetReview:distribution-contact.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:DatasetReview:metadata-contact.html.twig' %} +
    + +
    + {% include 'PelagosAppBundle:SideBySide:file-submit.html.twig' %} +
    +{% endblock %} diff --git a/src/Pelagos/Entity/Person.php b/src/Pelagos/Entity/Person.php index 47cf50e1b9..37fa70a13b 100644 --- a/src/Pelagos/Entity/Person.php +++ b/src/Pelagos/Entity/Person.php @@ -765,6 +765,20 @@ public function getDataRepositories() return $collection; } + /** + * Get a list of the names of all Data Repositories this person is associated with. + * + * @return array + */ + public function getDataRepositoryNames() + { + $dataRepositoryNames = array(); + foreach ($this->personDataRepositories as $personDataRepository) { + $dataRepositoryNames[] = $personDataRepository->getDataRepository()->getName(); + } + return $dataRepositoryNames; + } + /** * Setter for account. * diff --git a/src/Pelagos/Util/Geometry.php b/src/Pelagos/Util/Geometry.php index b308de80d1..74aa554641 100644 --- a/src/Pelagos/Util/Geometry.php +++ b/src/Pelagos/Util/Geometry.php @@ -38,6 +38,8 @@ public function __construct(EntityManager $entityManager) */ public function calculateEnvelopeFromGml($gml) { + $gml = GmlUtil::addNamespace($gml); + $sql = 'SELECT ST_AsText(ST_Envelope(ST_GeomFromGML(:gml, :srid)))'; $connection = $this->entityManager->getConnection(); $sth = $connection->prepare($sql); @@ -67,6 +69,8 @@ public function calculateEnvelopeFromGml($gml) */ public function calculateGeographicBoundsFromGml($gml) { + $gml = GmlUtil::addNamespace($gml); + $sql = 'SELECT ST_XMin(ST_GeomFromGml(:gml)) as "westBoundLongitude", ST_XMax(ST_GeomFromGml(:gml)) as "eastBoundLongitude",