diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Fields.php b/lib/Alchemy/Phrasea/Controller/Admin/Fields.php index 3968909c39..d2dd834a9a 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Fields.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Fields.php @@ -17,7 +17,7 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use PHPExiftool\Exception\TagUnknown; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class Fields implements ControllerProviderInterface { @@ -90,85 +90,51 @@ class Fields implements ControllerProviderInterface public function updateFields(Application $app, Request $request, $sbas_id) { - $json = array( - 'success' => false, - // use to store the updated collection - 'fields' => array(), - 'messages' => array() - ); - + $fields = array(); $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); + $metaStructure = $databox->get_meta_structure(); $connection = $databox->get_connection(); $data = $this->getFieldsJsonFromRequest($app, $request); - // calculate max position - try { - $stmt = $connection->prepare('SELECT MAX(sorter) as max_position FROM metadatas_structure'); - $stmt->execute(); - $row = $stmt->fetch(\PDO::FETCH_ASSOC); - $stmt->closeCursor(); - $maxPosition = $row['max_position'] + 1; - } catch (\PDOException $e) { - $app->abort(500); - } - $connection->beginTransaction(); - $i = 0; + foreach ($data as $jsonField) { try { - $jsonField['sorter'] = $jsonField['sorter'] + $maxPosition; $field = \databox_field::get_instance($app, $databox, $jsonField['id']); + + if ($field->get_name() !== $jsonField['name']) { + $this->validateNameField($metaStructure, $jsonField); + } + + $this->validateTagField($jsonField); + $this->updateFieldWithData($app, $field, $jsonField); $field->save(); - $json['fields'][] = $field->toArray(); - $i++; - } catch (\PDOException $e) { - if ($e->errorInfo[1] == 1062) { - $json['messages'][] = _(sprintf('Field name %s already exists', $jsonField['name'])); - } else { - $json['messages'][] = _(sprintf('Field %s could not be saved, please retry or contact an administrator if problem persists', $jsonField['name'])); - } + $fields[] = $field->toArray(); } catch (\Exception $e) { - if ($e instanceof \Exception_Databox_metadataDescriptionNotFound || $e->getPrevious() instanceof TagUnknown) { - $json['messages'][] = _(sprintf('Provided tag %s is unknown', $jsonField['tag'])); - } else { - $json['messages'][] = _(sprintf('Field %s could not be saved, please retry or contact an administrator if problem persists', $jsonField['name'])); - } + $connection->rollback(); + $app->abort(500, _(sprintf('Field %s could not be saved, please try again or contact an admin', $jsonField['name']))); + break; } } - if ($i === count($data)) { - // update field position in database, this query forces to update all fields each time - $stmt = $connection->prepare(sprintf('UPDATE metadatas_structure SET sorter = (sorter - %s)', $maxPosition)); - $row = $stmt->execute(); - $stmt->closeCursor(); + $connection->commit(); - $connection->commit(); - - $json['success'] = true; - $json['messages'][] = _('Fields configuration has been saved'); - - // update field position in array - array_walk($json['fields'], function(&$field) use ($maxPosition) { - $field['sorter'] = $field['sorter'] - $maxPosition; - }); - } else { - $connection->rollback(); - } - - return $app->json($json); + return $app->json($fields); } public function getLanguage(Application $app, Request $request) { return $app->json(array( - 'something_wrong' => _('Something wrong happened, please try again or contact an admin if problem persists'), + 'something_wrong' => _('Something wrong happened, please try again or contact an admin'), + 'created_success' => _('%s field has been created with success'), 'deleted_success' => _('%s field has been deleted with success'), 'are_you_sure_delete' => _('Do you really want to delete the field %s ?'), 'validation_blank' => _('Field can not be blank'), 'validation_name_exists' => _('Field name already exists'), 'validation_tag_invalid' => _('Field source is not valid'), 'field_error' => _('Field %s contains errors'), + 'fields_save' => _('Your configuration has been successfuly saved'), )); } @@ -245,40 +211,26 @@ class Fields implements ControllerProviderInterface } public function createField(Application $app, Request $request, $sbas_id) { - $json = array( - 'success' => false, - 'message' => _('Something wrong happened, please try again or contact an admin if problem persists'), - 'field' => array() - ); - $headers = array(); - $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); $data = $this->getFieldJsonFromRequest($app, $request); + $metaStructure = $databox->get_meta_structure(); + $this->validateNameField($metaStructure, $data); + $this->validateTagField($data); + try { $field = \databox_field::create($app, $databox, $data['name'], $data['multi']); - $this->updateFieldWithData($app, $field, $data); $field->save(); - - $json['success'] = true; - $headers['location'] = $app->path('admin_fields_show_field', array( - 'sbas_id' => $sbas_id, - 'id' => $field->get_id(), - )); - $json['message'] = _(sprintf('Tag name %s has been created successfully', $data['name'])); - $json['field'] = $field->toArray(); - } catch (\PDOException $e) { - if ($e->errorInfo[1] == 1062) { - $json['message'] = _(sprintf('Field name %s already exists', $data['name'])); - } } catch (\Exception $e) { - if ($e instanceof \Exception_Databox_metadataDescriptionNotFound || $e->getPrevious() instanceof TagUnknown) { - $json['message'] = _(sprintf('Provided tag %s is unknown', $data['tag'])); - } + $app->abort(500, _(sprintf('Field %s could not be created, please try again or contact an admin', $data['name']))); } - return $app->json($json, 201, $headers); + return $app->json($field->toArray(), 201, array( + 'location' => $app->path('admin_fields_show_field', array( + 'sbas_id' => $sbas_id, + 'id' => $field->get_id() + )))); } public function listFields(Application $app, $sbas_id) { @@ -301,6 +253,13 @@ class Fields implements ControllerProviderInterface $field = \databox_field::get_instance($app, $databox, $id); $data = $this->getFieldJsonFromRequest($app, $request); + $this->validateTagField($data); + + if ($field->get_name() !== $data['name']) { + $metaStructure = $databox->get_meta_structure(); + $this->validateNameField($metaStructure, $data); + } + $this->updateFieldWithData($app, $field, $data); $field->save(); @@ -317,13 +276,7 @@ class Fields implements ControllerProviderInterface private function getFieldJsonFromRequest(Application $app, Request $request) { - $body = $request->getContent(); - $data = @json_decode($body, true); - - if (JSON_ERROR_NONE !== json_last_error()) { - $app->abort(400, 'Body must contain a valid JSON payload'); - } - + $data = $this->requestBodyToJson($request); $required = $this->getMandatoryFieldProperties(); foreach ($required as $key) { @@ -337,13 +290,7 @@ class Fields implements ControllerProviderInterface private function getFieldsJsonFromRequest(Application $app, Request $request) { - $body = $request->getContent(); - $data = @json_decode($body, true); - - if (JSON_ERROR_NONE !== json_last_error()) { - $app->abort(400, 'Body must contain a valid JSON payload'); - } - + $data = $this->requestBodyToJson($request); $required = $this->getMandatoryFieldProperties(); foreach($data as $field) { @@ -386,14 +333,15 @@ class Fields implements ControllerProviderInterface } - $dces_element = null; + if ('' !== $dcesElement = (string) $data['dces-element']) { + $class = sprintf('\databox_Field_DCES_%s', $dcesElement); - $class = '\databox_Field_DCES_' . $data['dces-element']; - if (class_exists($class)) { - $dces_element = new $class(); + if (!class_exists($class)) { + throw new BadRequestHttpException(sprintf('DCES element %s does not exist', $dcesElement)); + } + + $field->set_dces_element(new $class()); } - - $field->set_dces_element($dces_element); } private function getMandatoryFieldProperties() @@ -404,4 +352,32 @@ class Fields implements ControllerProviderInterface 'vocabulary-type', 'vocabulary-restricted', 'dces-element' ); } + + private function validateNameField(\databox_descriptionStructure $metaStructure, array $field) + { + if (null !== $metaStructure->get_element_by_name($field['name'])) { + throw new BadRequestHttpException(_(sprintf('Field %s already exists', $field['name']))); + } + } + + private function validateTagField(array $field) + { + try { + \databox_field::loadClassFromTagName($field['tag']); + } catch(\Exception_Databox_metadataDescriptionNotFound $e) { + throw new BadRequestHttpException(_(sprintf('Provided tag %s is unknown', $field['tag']))); + } + } + + private function requestBodyToJson(Request $request) + { + $body = $request->getContent(); + $data = @json_decode($body, true); + + if (JSON_ERROR_NONE !== json_last_error()) { + throw new BadRequestHttpException('Body must contain a valid JSON payload'); + } + + return $data; + } } diff --git a/lib/classes/databox/descriptionStructure.php b/lib/classes/databox/descriptionStructure.php index 4445b00219..1aa64f287a 100644 --- a/lib/classes/databox/descriptionStructure.php +++ b/lib/classes/databox/descriptionStructure.php @@ -14,7 +14,7 @@ * @license http://opensource.org/licenses/gpl-3.0 GPLv3 * @link www.phraseanet.com */ -class databox_descriptionStructure implements IteratorAggregate +class databox_descriptionStructure implements IteratorAggregate, Countable { /** * @@ -138,4 +138,13 @@ class databox_descriptionStructure implements IteratorAggregate return $element->toArray(); }, array_values($this->elements)); } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->elements); + } } + diff --git a/lib/classes/databox/field.php b/lib/classes/databox/field.php index bba7f1b6c4..1bf224538b 100644 --- a/lib/classes/databox/field.php +++ b/lib/classes/databox/field.php @@ -175,7 +175,11 @@ class databox_field implements cache_cacheableInterface $connbas = $this->get_connection(); - $sql = "SELECT * FROM metadatas_structure WHERE id=:id"; + $sql = "SELECT `thumbtitle`, `separator`, `dces_element`, `tbranch`, + `type`, `report`, `multi`, `required`, `readonly`, `indexable`, + `name`, `src`, `business`, `VocabularyControlType`, + `RestrictToVocabularyControl`, `sorter` + FROM metadatas_structure WHERE id=:id"; $stmt = $connbas->prepare($sql); $stmt->execute(array(':id' => $id)); @@ -805,21 +809,21 @@ class databox_field implements cache_cacheableInterface } /** - * * @return string */ public function get_name() { return $this->name; } + /** - * * @return string */ public function get_position() { return $this->position; } + /** * * @return string @@ -859,7 +863,7 @@ class databox_field implements cache_cacheableInterface 'readonly' => $this->readonly, 'multi' => $this->multi, 'indexable' => $this->indexable, - 'dces-element' => $this->dces_element ? $this->dces_element->get_label(): null, + 'dces-element' => $this->dces_element ? $this->dces_element->get_label() : null, 'vocabulary-type' => $this->Vocabulary ? $this->Vocabulary->getType() : null, 'vocabulary-restricted' => $this->VocabularyRestriction, ); diff --git a/lib/conf.d/bases_structure.xml b/lib/conf.d/bases_structure.xml index 071b5b7098..744c5818c8 100644 --- a/lib/conf.d/bases_structure.xml +++ b/lib/conf.d/bases_structure.xml @@ -4748,13 +4748,6 @@ required - - sorter - UNIQUE - - sorter - - InnoDB diff --git a/templates/web/admin/fields/index.html.twig b/templates/web/admin/fields/index.html.twig index 80c6c171b1..a841cd8ae6 100644 --- a/templates/web/admin/fields/index.html.twig +++ b/templates/web/admin/fields/index.html.twig @@ -8,7 +8,7 @@
{# set loading state, this will be removed once backbone application is fully loaded #} - {% trans %}Loading database documentary fields ...{% endtrans %} + {% trans %}Loading database documentary structure ...{% endtrans %}
@@ -21,5 +21,4 @@
{# bootstrap admin field backbone application #} - - + diff --git a/templates/web/admin/fields/templates.html.twig b/templates/web/admin/fields/templates.html.twig index c5b8f43096..503ff0ef14 100644 --- a/templates/web/admin/fields/templates.html.twig +++ b/templates/web/admin/fields/templates.html.twig @@ -120,7 +120,7 @@
-

{% trans %}Advanced field parameters{% endtrans %}

+

{% trans %}Advanced field parameters{% endtrans %}

@@ -172,7 +172,7 @@
-

{% trans %}Display & action settings{% endtrans %}

+

{% trans %}Display & action settings{% endtrans %}

diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php index 764cd5436b..54263c9cee 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Admin/FieldsTest.php @@ -198,12 +198,10 @@ class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $data = json_decode($response, true); - $this->assertArrayHasKey('success', $data); - $this->assertArrayHasKey('messages', $data); - $this->assertArrayHasKey('fields', $data); + $this->assertTrue(is_array($data)); // expect last 2 fields from body equals last 2 fields from response - $this->assertEquals(array_splice($body, -2), array_splice($data['fields'], -2)); + $this->assertEquals(array_splice($body, -2), array_splice($data, -2)); // delete created fields foreach($fieldObjects as $field) { @@ -242,17 +240,15 @@ class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract $data = json_decode($response, true); - $this->assertArrayHasKey('success', $data); - $this->assertArrayHasKey('message', $data); - $this->assertArrayHasKey('field', $data); + $this->assertTrue(is_array($data)); - $dataWithoutIds = $data['field']; + $dataWithoutIds = $data; unset($dataWithoutIds['id']); unset($dataWithoutIds['sorter']); $this->assertEquals(json_decode($body, true), $dataWithoutIds); - $field = \databox_field::get_instance(self::$DI['app'], $databox, $data['field']['id']); + $field = \databox_field::get_instance(self::$DI['app'], $databox, $data['id']); $field->delete(); } diff --git a/www/scripts/apps/admin/fields/app.js b/www/scripts/apps/admin/fields/app.js index 49606f80e0..3916b05b8a 100644 --- a/www/scripts/apps/admin/fields/app.js +++ b/www/scripts/apps/admin/fields/app.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/collections/dcFields.js b/www/scripts/apps/admin/fields/collections/dcFields.js index f3478d16ff..29f78c4885 100644 --- a/www/scripts/apps/admin/fields/collections/dcFields.js +++ b/www/scripts/apps/admin/fields/collections/dcFields.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone", diff --git a/www/scripts/apps/admin/fields/collections/fields.js b/www/scripts/apps/admin/fields/collections/fields.js index 3ede7855c4..b472035fb3 100644 --- a/www/scripts/apps/admin/fields/collections/fields.js +++ b/www/scripts/apps/admin/fields/collections/fields.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone", diff --git a/www/scripts/apps/admin/fields/collections/vocabularies.js b/www/scripts/apps/admin/fields/collections/vocabularies.js index 290348ce41..75a6afe6ec 100644 --- a/www/scripts/apps/admin/fields/collections/vocabularies.js +++ b/www/scripts/apps/admin/fields/collections/vocabularies.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone", diff --git a/www/scripts/apps/admin/fields/errors/error.js b/www/scripts/apps/admin/fields/errors/error.js index 97d234a5ce..1e8b734978 100644 --- a/www/scripts/apps/admin/fields/errors/error.js +++ b/www/scripts/apps/admin/fields/errors/error.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore" diff --git a/www/scripts/apps/admin/fields/errors/errorManager.js b/www/scripts/apps/admin/fields/errors/errorManager.js index aea7bc4067..d87a636db0 100644 --- a/www/scripts/apps/admin/fields/errors/errorManager.js +++ b/www/scripts/apps/admin/fields/errors/errorManager.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/errors/errorModel.js b/www/scripts/apps/admin/fields/errors/errorModel.js index ebe1399f73..50e5108e52 100644 --- a/www/scripts/apps/admin/fields/errors/errorModel.js +++ b/www/scripts/apps/admin/fields/errors/errorModel.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore" diff --git a/www/scripts/apps/admin/fields/main.js b/www/scripts/apps/admin/fields/main.js index f97be950bf..fbf125aeb1 100644 --- a/www/scripts/apps/admin/fields/main.js +++ b/www/scripts/apps/admin/fields/main.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + // configure AMD loading require.config({ baseUrl: "/scripts", diff --git a/www/scripts/apps/admin/fields/views.js b/www/scripts/apps/admin/fields/views.js index c180e31a22..eff28845bb 100644 --- a/www/scripts/apps/admin/fields/views.js +++ b/www/scripts/apps/admin/fields/views.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/views/alert.js b/www/scripts/apps/admin/fields/views/alert.js index 1f46e44898..f075b1e098 100644 --- a/www/scripts/apps/admin/fields/views/alert.js +++ b/www/scripts/apps/admin/fields/views/alert.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/views/create.js b/www/scripts/apps/admin/fields/views/create.js index 99c93b0715..ad5d2d19c3 100644 --- a/www/scripts/apps/admin/fields/views/create.js +++ b/www/scripts/apps/admin/fields/views/create.js @@ -1,10 +1,19 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", "backbone", "i18n", "bootstrap", - "apps/admin/fields/views/create", + "apps/admin/fields/views/alert", "models/field" ], function($, _, Backbone, i18n, bootstrap, AlertView, FieldModel) { var CreateView = Backbone.View.extend({ @@ -105,18 +114,18 @@ define([ field.save(null, { success: function(field, response, options) { - if (response.success) { - AdminFieldApp.fieldsCollection.add(field); - _.last(self.itemViews).clickAction().animate(); - } + AdminFieldApp.fieldsCollection.add(field); + _.last(AdminFieldApp.fieldListView.itemViews).clickAction().animate(); - new AlertView({ - alert: response.success ? "success" : "error", message: response.message + new AlertView({alert: "info", message: i18n.t("created_success", { + postProcess: "sprintf", + sprintf: [field.get("name")] + }) }).render(); }, - error: function(model, xhr, options) { + error: function(xhr, textStatus, errorThrown) { new AlertView({ - alert: "error", message: i18n.t("something_wrong")} + alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong")} ).render(); self.toggleCreateFormAction(); diff --git a/www/scripts/apps/admin/fields/views/dcField.js b/www/scripts/apps/admin/fields/views/dcField.js index 69cbe490f9..369309ed31 100644 --- a/www/scripts/apps/admin/fields/views/dcField.js +++ b/www/scripts/apps/admin/fields/views/dcField.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", @@ -17,7 +26,7 @@ define([ }); this.$el.html(template); - + var index = $("#dces-element", this.$el)[0].selectedIndex - 1; if (index > 0 ) { $(".dces-help-block", AdminFieldApp.$rightBlock).html( diff --git a/www/scripts/apps/admin/fields/views/edit.js b/www/scripts/apps/admin/fields/views/edit.js index 9eca7271e3..1b3adc2200 100644 --- a/www/scripts/apps/admin/fields/views/edit.js +++ b/www/scripts/apps/admin/fields/views/edit.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", @@ -64,31 +73,6 @@ define([ var fieldTagId = fieldTag.attr("id"); var fieldTagValue = fieldTag.val(); - // check for format tag - if ("" !== fieldTagValue && false === /[a-z]+:[a-z0-9]+/i.test(fieldTagValue)) { - fieldTag - .closest(".control-group") - .addClass("error") - .find(".help-block") - .empty() - .append(i18n.t("validation_tag_invalid")); - // add error - AdminFieldApp.errorManager.addModelFieldError(new Error( - self.model, fieldTagId, i18n.t("validation_tag_invalid") - )); - } else if (fieldTag.closest(".control-group").hasClass("error")) { - // remove error - AdminFieldApp.errorManager.removeModelFieldError( - self.model, fieldTagId - ); - - fieldTag - .closest(".control-group") - .removeClass("error") - .find(".help-block") - .empty(); - } - var data = {}; data[fieldTagId] = fieldTagValue; self.model.set(data); @@ -109,6 +93,7 @@ define([ "click .delete-field": "deleteAction", "keyup #name": "changeNameAction", "focusout input[type=text]": "fieldChangedAction", + "focusout input#tag": "tagFieldChangedAction", "change input[type=checkbox]": "fieldChangedAction", "change select": "selectionChangedAction" }, @@ -170,6 +155,38 @@ define([ return this; }, + tagFieldChangedAction: function(e) { + var fieldTag = $(e.target); + var fieldTagId = fieldTag.attr("id"); + var fieldTagValue = fieldTag.val(); + + // check for format tag + if ("" !== fieldTagValue && false === /[a-z]+:[a-z0-9]+/i.test(fieldTagValue)) { + fieldTag + .closest(".control-group") + .addClass("error") + .find(".help-block") + .empty() + .append(i18n.t("validation_tag_invalid")); + // add error + AdminFieldApp.errorManager.addModelFieldError(new Error( + this.model, fieldTagId, i18n.t("validation_tag_invalid") + )); + } else if (fieldTag.closest(".control-group").hasClass("error")) { + // remove error + AdminFieldApp.errorManager.removeModelFieldError( + this.model, fieldTagId + ); + + fieldTag + .closest(".control-group") + .removeClass("error") + .find(".help-block") + .empty(); + } + + this.fieldChangedAction(e); + }, deleteAction: function() { var self = this; var modalView = new ModalView({ @@ -200,9 +217,9 @@ define([ }) }).render(); }, - error: function(model, xhr) { + error: function(xhr, textStatus, errorThrown) { new AlertView({ - alert: "error", message: i18n.t("something_wrong") + alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong") }).render(); } }); @@ -213,12 +230,13 @@ define([ _onModelChange: function() { AdminFieldApp.fieldListView.collection.remove(this.model, {silent: true}); AdminFieldApp.fieldListView.collection.add(this.model); - var index = AdminFieldApp.fieldListView.collection.indexOf(this.model); this._selectModelView(index); this.render(); + + AdminFieldApp.saveView.updateStateButton(); }, // select temView by index in itemList _selectModelView: function(index) { diff --git a/www/scripts/apps/admin/fields/views/fieldError.js b/www/scripts/apps/admin/fields/views/fieldError.js index 38137523a8..bfa6dcc3be 100644 --- a/www/scripts/apps/admin/fields/views/fieldError.js +++ b/www/scripts/apps/admin/fields/views/fieldError.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/views/list.js b/www/scripts/apps/admin/fields/views/list.js index 4605bb7ec5..0767d781f6 100644 --- a/www/scripts/apps/admin/fields/views/list.js +++ b/www/scripts/apps/admin/fields/views/list.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "jqueryui", @@ -85,7 +94,6 @@ define([ placeholder: "item-list-placeholder", start: function(event, ui) { ui.item.addClass("border-bottom"); - }, stop: function(event, ui) { ui.firstItemPosition = $("li:first", $(this).sortable('widget')).position().top; @@ -105,18 +113,18 @@ define([ return this; }, updateSortAction: function(event, model, ui) { - var position = ui.item.index(); - this.collection.remove(model, {silent: true}); + var newPosition = ui.item.index(); + var curPosition = this.collection.indexOf(model); // reorder all collection model - this.collection.each(function(model, index) { - var ordinal = index; - if (index >= position) ordinal += 1; - model.set("sorter", ordinal); + this.collection.each(function(el, index) { + if (newPosition > curPosition && (index > curPosition && index <= curPosition)) index -= 1; + else if (newPosition < curPosition && (index >= curPosition && index < curPosition)) index += 1; + + el.set("sorter", index); }); - model.set("sorter", position); - this.collection.add(model, {at: position}); + this.render(); this.itemViews[0].animate(Math.abs(ui.firstItemPosition)); @@ -125,6 +133,8 @@ define([ return el.get("id") === AdminFieldApp.fieldEditView.model.get("id"); }); + AdminFieldApp.saveView.updateStateButton(); + return this; } })); diff --git a/www/scripts/apps/admin/fields/views/listRow.js b/www/scripts/apps/admin/fields/views/listRow.js index 1bec7d2a00..e8c76da3d7 100644 --- a/www/scripts/apps/admin/fields/views/listRow.js +++ b/www/scripts/apps/admin/fields/views/listRow.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/views/modal.js b/www/scripts/apps/admin/fields/views/modal.js index d6fc73b99a..49e503e7e5 100644 --- a/www/scripts/apps/admin/fields/views/modal.js +++ b/www/scripts/apps/admin/fields/views/modal.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", diff --git a/www/scripts/apps/admin/fields/views/save.js b/www/scripts/apps/admin/fields/views/save.js index bf993075d4..6645f123f1 100644 --- a/www/scripts/apps/admin/fields/views/save.js +++ b/www/scripts/apps/admin/fields/views/save.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "jquery", "underscore", @@ -29,20 +38,18 @@ define([ if (this._isModelDesync()) { this._loadingState(true); AdminFieldApp.fieldsCollection.save({ - success: function(response) { + success: function(fields) { // reset collection with new one - if (response.success) { - AdminFieldApp.fieldsCollection.reset(response.fields); - } + AdminFieldApp.fieldsCollection.reset(fields); new AlertView({ - alert: response.success ? "success" : "error", - message: response.messages.join("
") + alert: "success", + message: i18n.t("fields_save") }).render(); }, - error: function(model, xhr, options) { + error: function(xhr, textStatus, errorThrown) { new AlertView({ - alert: "error", message: i18n.t("something_wrong") + alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong") }).render(); } }).done(function() { @@ -55,9 +62,13 @@ define([ render: function () { var template = _.template($("#save_template").html()); this.$el.html(template); + this.updateStateButton(); return this; }, + updateStateButton: function() { + this._disableSaveButton(!this._isModelDesync()); + }, // check whether model has changed or not _isModelDesync: function () { return "undefined" !== typeof AdminFieldApp.fieldsCollection.find(function(model) { @@ -86,7 +97,7 @@ define([ $(".save-block", AdminFieldApp.$top).removeClass("loading"); } - this._disableSaveButton(active); + this.updateStateButton(); this._overlay(active); } }); diff --git a/www/scripts/models/dcField.js b/www/scripts/models/dcField.js index 8c60b959cc..65cec8e6b6 100644 --- a/www/scripts/models/dcField.js +++ b/www/scripts/models/dcField.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone" diff --git a/www/scripts/models/field.js b/www/scripts/models/field.js index 55e2c847a7..84a746f5f1 100644 --- a/www/scripts/models/field.js +++ b/www/scripts/models/field.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone" diff --git a/www/scripts/models/vocabulary.js b/www/scripts/models/vocabulary.js index 1eb31e0bd1..e681dc98b5 100644 --- a/www/scripts/models/vocabulary.js +++ b/www/scripts/models/vocabulary.js @@ -1,3 +1,12 @@ +/* + * This file is part of Phraseanet + * + * (c) 2005-2013 Alchemy + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + define([ "underscore", "backbone" diff --git a/www/scripts/tests/fixtures/admin/fields/dom b/www/scripts/tests/fixtures/admin/fields/dom index 1e549a64b9..f7f354628f 100644 --- a/www/scripts/tests/fixtures/admin/fields/dom +++ b/www/scripts/tests/fixtures/admin/fields/dom @@ -15,4 +15,4 @@
- \ No newline at end of file + diff --git a/www/scripts/tests/specs/admin/fields.js b/www/scripts/tests/specs/admin/fields.js index 2869f0bbd5..9824b523a8 100644 --- a/www/scripts/tests/specs/admin/fields.js +++ b/www/scripts/tests/specs/admin/fields.js @@ -287,6 +287,14 @@ define([ AdminFieldApp.fieldListView.render(); }); + it("should update collection when model change", function() { + AdminFieldApp.fieldListView.itemViews[0].clickAction(); + AdminFieldApp.fieldEditView.model.set({ + "name": "new name" + }); + assert.equal(AdminFieldApp.fieldListView.itemViews[0].model.get('name'), "new name", 'model is updated'); + }); + it("should update edit view when clicking on single element", function() { AdminFieldApp.fieldListView.itemViews[0].clickAction(); should.exist(AdminFieldApp.fieldEditView); @@ -296,15 +304,7 @@ define([ it("should reorder collection on drop action", function() { var ui = {item: {index: function() {return 2;}}}; AdminFieldApp.fieldListView.itemViews[0].dropAction({},ui); - assert.equal(AdminFieldApp.fieldListView.collection.last().get('sorter'), 2, 'model is updated'); - }); - - it("should update collection when model change", function() { - AdminFieldApp.fieldListView.itemViews[0].clickAction(); - AdminFieldApp.fieldEditView.model.set({ - "name": "new name" - }); - assert.equal(AdminFieldApp.fieldListView.collection.first().get('name'), "new name", 'model is updated'); + assert.equal(AdminFieldApp.fieldListView.collection.last().get('sorter'), 3, 'model is updated'); }); }); });
@@ -194,7 +194,7 @@