Address PR comment's

Fix tests
This commit is contained in:
Nicolas Le Goff
2013-05-27 18:19:44 +02:00
committed by Romain Neutron
parent 848413dead
commit b52e4ee8c0
30 changed files with 369 additions and 191 deletions

View File

@@ -17,7 +17,7 @@ use Silex\Application;
use Silex\ControllerProviderInterface; use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use PHPExiftool\Exception\TagUnknown; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class Fields implements ControllerProviderInterface class Fields implements ControllerProviderInterface
{ {
@@ -90,85 +90,51 @@ class Fields implements ControllerProviderInterface
public function updateFields(Application $app, Request $request, $sbas_id) public function updateFields(Application $app, Request $request, $sbas_id)
{ {
$json = array( $fields = array();
'success' => false,
// use to store the updated collection
'fields' => array(),
'messages' => array()
);
$databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id); $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id);
$metaStructure = $databox->get_meta_structure();
$connection = $databox->get_connection(); $connection = $databox->get_connection();
$data = $this->getFieldsJsonFromRequest($app, $request); $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(); $connection->beginTransaction();
$i = 0;
foreach ($data as $jsonField) { foreach ($data as $jsonField) {
try { try {
$jsonField['sorter'] = $jsonField['sorter'] + $maxPosition;
$field = \databox_field::get_instance($app, $databox, $jsonField['id']); $field = \databox_field::get_instance($app, $databox, $jsonField['id']);
$this->updateFieldWithData($app, $field, $jsonField);
$field->save(); if ($field->get_name() !== $jsonField['name']) {
$json['fields'][] = $field->toArray(); $this->validateNameField($metaStructure, $jsonField);
$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']));
}
} 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']));
}
}
} }
if ($i === count($data)) { $this->validateTagField($jsonField);
// 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)); $this->updateFieldWithData($app, $field, $jsonField);
$row = $stmt->execute(); $field->save();
$stmt->closeCursor(); $fields[] = $field->toArray();
} catch (\Exception $e) {
$connection->rollback();
$app->abort(500, _(sprintf('Field %s could not be saved, please try again or contact an admin', $jsonField['name'])));
break;
}
}
$connection->commit(); $connection->commit();
$json['success'] = true; return $app->json($fields);
$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);
} }
public function getLanguage(Application $app, Request $request) public function getLanguage(Application $app, Request $request)
{ {
return $app->json(array( 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'), 'deleted_success' => _('%s field has been deleted with success'),
'are_you_sure_delete' => _('Do you really want to delete the field %s ?'), 'are_you_sure_delete' => _('Do you really want to delete the field %s ?'),
'validation_blank' => _('Field can not be blank'), 'validation_blank' => _('Field can not be blank'),
'validation_name_exists' => _('Field name already exists'), 'validation_name_exists' => _('Field name already exists'),
'validation_tag_invalid' => _('Field source is not valid'), 'validation_tag_invalid' => _('Field source is not valid'),
'field_error' => _('Field %s contains errors'), '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) { 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); $databox = $app['phraseanet.appbox']->get_databox((int) $sbas_id);
$data = $this->getFieldJsonFromRequest($app, $request); $data = $this->getFieldJsonFromRequest($app, $request);
$metaStructure = $databox->get_meta_structure();
$this->validateNameField($metaStructure, $data);
$this->validateTagField($data);
try { try {
$field = \databox_field::create($app, $databox, $data['name'], $data['multi']); $field = \databox_field::create($app, $databox, $data['name'], $data['multi']);
$this->updateFieldWithData($app, $field, $data); $this->updateFieldWithData($app, $field, $data);
$field->save(); $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) { } catch (\Exception $e) {
if ($e instanceof \Exception_Databox_metadataDescriptionNotFound || $e->getPrevious() instanceof TagUnknown) { $app->abort(500, _(sprintf('Field %s could not be created, please try again or contact an admin', $data['name'])));
$json['message'] = _(sprintf('Provided tag %s is unknown', $data['tag']));
}
} }
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) { public function listFields(Application $app, $sbas_id) {
@@ -301,6 +253,13 @@ class Fields implements ControllerProviderInterface
$field = \databox_field::get_instance($app, $databox, $id); $field = \databox_field::get_instance($app, $databox, $id);
$data = $this->getFieldJsonFromRequest($app, $request); $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); $this->updateFieldWithData($app, $field, $data);
$field->save(); $field->save();
@@ -317,13 +276,7 @@ class Fields implements ControllerProviderInterface
private function getFieldJsonFromRequest(Application $app, Request $request) private function getFieldJsonFromRequest(Application $app, Request $request)
{ {
$body = $request->getContent(); $data = $this->requestBodyToJson($request);
$data = @json_decode($body, true);
if (JSON_ERROR_NONE !== json_last_error()) {
$app->abort(400, 'Body must contain a valid JSON payload');
}
$required = $this->getMandatoryFieldProperties(); $required = $this->getMandatoryFieldProperties();
foreach ($required as $key) { foreach ($required as $key) {
@@ -337,13 +290,7 @@ class Fields implements ControllerProviderInterface
private function getFieldsJsonFromRequest(Application $app, Request $request) private function getFieldsJsonFromRequest(Application $app, Request $request)
{ {
$body = $request->getContent(); $data = $this->requestBodyToJson($request);
$data = @json_decode($body, true);
if (JSON_ERROR_NONE !== json_last_error()) {
$app->abort(400, 'Body must contain a valid JSON payload');
}
$required = $this->getMandatoryFieldProperties(); $required = $this->getMandatoryFieldProperties();
foreach($data as $field) { 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)) {
if (class_exists($class)) { throw new BadRequestHttpException(sprintf('DCES element %s does not exist', $dcesElement));
$dces_element = new $class();
} }
$field->set_dces_element($dces_element); $field->set_dces_element(new $class());
}
} }
private function getMandatoryFieldProperties() private function getMandatoryFieldProperties()
@@ -404,4 +352,32 @@ class Fields implements ControllerProviderInterface
'vocabulary-type', 'vocabulary-restricted', 'dces-element' '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;
}
} }

View File

@@ -14,7 +14,7 @@
* @license http://opensource.org/licenses/gpl-3.0 GPLv3 * @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com * @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(); return $element->toArray();
}, array_values($this->elements)); }, array_values($this->elements));
} }
/**
* {@inheritdoc}
*/
public function count()
{
return count($this->elements);
}
} }

View File

@@ -175,7 +175,11 @@ class databox_field implements cache_cacheableInterface
$connbas = $this->get_connection(); $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 = $connbas->prepare($sql);
$stmt->execute(array(':id' => $id)); $stmt->execute(array(':id' => $id));
@@ -805,21 +809,21 @@ class databox_field implements cache_cacheableInterface
} }
/** /**
*
* @return string * @return string
*/ */
public function get_name() public function get_name()
{ {
return $this->name; return $this->name;
} }
/** /**
*
* @return string * @return string
*/ */
public function get_position() public function get_position()
{ {
return $this->position; return $this->position;
} }
/** /**
* *
* @return string * @return string
@@ -859,7 +863,7 @@ class databox_field implements cache_cacheableInterface
'readonly' => $this->readonly, 'readonly' => $this->readonly,
'multi' => $this->multi, 'multi' => $this->multi,
'indexable' => $this->indexable, '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-type' => $this->Vocabulary ? $this->Vocabulary->getType() : null,
'vocabulary-restricted' => $this->VocabularyRestriction, 'vocabulary-restricted' => $this->VocabularyRestriction,
); );

View File

@@ -4748,13 +4748,6 @@
<field>required</field> <field>required</field>
</fields> </fields>
</index> </index>
<index>
<name>sorter</name>
<type>UNIQUE</type>
<fields>
<field>sorter</field>
</fields>
</index>
</indexes> </indexes>
<engine>InnoDB</engine> <engine>InnoDB</engine>
</table> </table>

View File

@@ -8,7 +8,7 @@
<div class="span4 save-block"> <div class="span4 save-block">
{# set loading state, this will be removed once backbone application is fully loaded #} {# set loading state, this will be removed once backbone application is fully loaded #}
<img src="/skins/icons/loaderFFF.gif"/> <img src="/skins/icons/loaderFFF.gif"/>
{% trans %}Loading database documentary fields ...{% endtrans %} {% trans %}Loading database documentary structure ...{% endtrans %}
</div> </div>
<div class="span8"> <div class="span8">
<div class="block-alert"></div> <div class="block-alert"></div>
@@ -21,5 +21,4 @@
</div> </div>
{# bootstrap admin field backbone application #} {# bootstrap admin field backbone application #}
<script src="/assets/requirejs/require.js"></script> <script type="text/javascript" src="/include/minify/f=/assets/requirejs/require.js,/scripts/apps/admin/fields/main.js"></script>
<script src="/scripts/apps/admin/fields/main.js"></script>

View File

@@ -198,12 +198,10 @@ class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
$data = json_decode($response, true); $data = json_decode($response, true);
$this->assertArrayHasKey('success', $data); $this->assertTrue(is_array($data));
$this->assertArrayHasKey('messages', $data);
$this->assertArrayHasKey('fields', $data);
// expect last 2 fields from body equals last 2 fields from response // 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 // delete created fields
foreach($fieldObjects as $field) { foreach($fieldObjects as $field) {
@@ -242,17 +240,15 @@ class ControllerFieldsTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
$data = json_decode($response, true); $data = json_decode($response, true);
$this->assertArrayHasKey('success', $data); $this->assertTrue(is_array($data));
$this->assertArrayHasKey('message', $data);
$this->assertArrayHasKey('field', $data);
$dataWithoutIds = $data['field']; $dataWithoutIds = $data;
unset($dataWithoutIds['id']); unset($dataWithoutIds['id']);
unset($dataWithoutIds['sorter']); unset($dataWithoutIds['sorter']);
$this->assertEquals(json_decode($body, true), $dataWithoutIds); $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(); $field->delete();
} }

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"underscore", "underscore",
"backbone", "backbone",

View File

@@ -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([ define([
"underscore", "underscore",
"backbone", "backbone",

View File

@@ -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([ define([
"underscore", "underscore",
"backbone", "backbone",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore" "underscore"

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore" "underscore"

View File

@@ -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 // configure AMD loading
require.config({ require.config({
baseUrl: "/scripts", baseUrl: "/scripts",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",
"backbone", "backbone",
"i18n", "i18n",
"bootstrap", "bootstrap",
"apps/admin/fields/views/create", "apps/admin/fields/views/alert",
"models/field" "models/field"
], function($, _, Backbone, i18n, bootstrap, AlertView, FieldModel) { ], function($, _, Backbone, i18n, bootstrap, AlertView, FieldModel) {
var CreateView = Backbone.View.extend({ var CreateView = Backbone.View.extend({
@@ -105,18 +114,18 @@ define([
field.save(null, { field.save(null, {
success: function(field, response, options) { success: function(field, response, options) {
if (response.success) {
AdminFieldApp.fieldsCollection.add(field); AdminFieldApp.fieldsCollection.add(field);
_.last(self.itemViews).clickAction().animate(); _.last(AdminFieldApp.fieldListView.itemViews).clickAction().animate();
}
new AlertView({ new AlertView({alert: "info", message: i18n.t("created_success", {
alert: response.success ? "success" : "error", message: response.message postProcess: "sprintf",
sprintf: [field.get("name")]
})
}).render(); }).render();
}, },
error: function(model, xhr, options) { error: function(xhr, textStatus, errorThrown) {
new AlertView({ new AlertView({
alert: "error", message: i18n.t("something_wrong")} alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong")}
).render(); ).render();
self.toggleCreateFormAction(); self.toggleCreateFormAction();

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",
@@ -64,31 +73,6 @@ define([
var fieldTagId = fieldTag.attr("id"); var fieldTagId = fieldTag.attr("id");
var fieldTagValue = fieldTag.val(); 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 = {}; var data = {};
data[fieldTagId] = fieldTagValue; data[fieldTagId] = fieldTagValue;
self.model.set(data); self.model.set(data);
@@ -109,6 +93,7 @@ define([
"click .delete-field": "deleteAction", "click .delete-field": "deleteAction",
"keyup #name": "changeNameAction", "keyup #name": "changeNameAction",
"focusout input[type=text]": "fieldChangedAction", "focusout input[type=text]": "fieldChangedAction",
"focusout input#tag": "tagFieldChangedAction",
"change input[type=checkbox]": "fieldChangedAction", "change input[type=checkbox]": "fieldChangedAction",
"change select": "selectionChangedAction" "change select": "selectionChangedAction"
}, },
@@ -170,6 +155,38 @@ define([
return this; 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() { deleteAction: function() {
var self = this; var self = this;
var modalView = new ModalView({ var modalView = new ModalView({
@@ -200,9 +217,9 @@ define([
}) })
}).render(); }).render();
}, },
error: function(model, xhr) { error: function(xhr, textStatus, errorThrown) {
new AlertView({ new AlertView({
alert: "error", message: i18n.t("something_wrong") alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong")
}).render(); }).render();
} }
}); });
@@ -213,12 +230,13 @@ define([
_onModelChange: function() { _onModelChange: function() {
AdminFieldApp.fieldListView.collection.remove(this.model, {silent: true}); AdminFieldApp.fieldListView.collection.remove(this.model, {silent: true});
AdminFieldApp.fieldListView.collection.add(this.model); AdminFieldApp.fieldListView.collection.add(this.model);
var index = AdminFieldApp.fieldListView.collection.indexOf(this.model); var index = AdminFieldApp.fieldListView.collection.indexOf(this.model);
this._selectModelView(index); this._selectModelView(index);
this.render(); this.render();
AdminFieldApp.saveView.updateStateButton();
}, },
// select temView by index in itemList // select temView by index in itemList
_selectModelView: function(index) { _selectModelView: function(index) {

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"jqueryui", "jqueryui",
@@ -85,7 +94,6 @@ define([
placeholder: "item-list-placeholder", placeholder: "item-list-placeholder",
start: function(event, ui) { start: function(event, ui) {
ui.item.addClass("border-bottom"); ui.item.addClass("border-bottom");
}, },
stop: function(event, ui) { stop: function(event, ui) {
ui.firstItemPosition = $("li:first", $(this).sortable('widget')).position().top; ui.firstItemPosition = $("li:first", $(this).sortable('widget')).position().top;
@@ -105,18 +113,18 @@ define([
return this; return this;
}, },
updateSortAction: function(event, model, ui) { updateSortAction: function(event, model, ui) {
var position = ui.item.index(); var newPosition = ui.item.index();
this.collection.remove(model, {silent: true}); var curPosition = this.collection.indexOf(model);
// reorder all collection model // reorder all collection model
this.collection.each(function(model, index) { this.collection.each(function(el, index) {
var ordinal = index; if (newPosition > curPosition && (index > curPosition && index <= curPosition)) index -= 1;
if (index >= position) ordinal += 1; else if (newPosition < curPosition && (index >= curPosition && index < curPosition)) index += 1;
model.set("sorter", ordinal);
el.set("sorter", index);
}); });
model.set("sorter", position); this.render();
this.collection.add(model, {at: position});
this.itemViews[0].animate(Math.abs(ui.firstItemPosition)); this.itemViews[0].animate(Math.abs(ui.firstItemPosition));
@@ -125,6 +133,8 @@ define([
return el.get("id") === AdminFieldApp.fieldEditView.model.get("id"); return el.get("id") === AdminFieldApp.fieldEditView.model.get("id");
}); });
AdminFieldApp.saveView.updateStateButton();
return this; return this;
} }
})); }));

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",

View File

@@ -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([ define([
"jquery", "jquery",
"underscore", "underscore",
@@ -29,20 +38,18 @@ define([
if (this._isModelDesync()) { if (this._isModelDesync()) {
this._loadingState(true); this._loadingState(true);
AdminFieldApp.fieldsCollection.save({ AdminFieldApp.fieldsCollection.save({
success: function(response) { success: function(fields) {
// reset collection with new one // reset collection with new one
if (response.success) { AdminFieldApp.fieldsCollection.reset(fields);
AdminFieldApp.fieldsCollection.reset(response.fields);
}
new AlertView({ new AlertView({
alert: response.success ? "success" : "error", alert: "success",
message: response.messages.join("<br />") message: i18n.t("fields_save")
}).render(); }).render();
}, },
error: function(model, xhr, options) { error: function(xhr, textStatus, errorThrown) {
new AlertView({ new AlertView({
alert: "error", message: i18n.t("something_wrong") alert: "error", message: '' !== xhr.responseText ? xhr.responseText : i18n.t("something_wrong")
}).render(); }).render();
} }
}).done(function() { }).done(function() {
@@ -55,9 +62,13 @@ define([
render: function () { render: function () {
var template = _.template($("#save_template").html()); var template = _.template($("#save_template").html());
this.$el.html(template); this.$el.html(template);
this.updateStateButton();
return this; return this;
}, },
updateStateButton: function() {
this._disableSaveButton(!this._isModelDesync());
},
// check whether model has changed or not // check whether model has changed or not
_isModelDesync: function () { _isModelDesync: function () {
return "undefined" !== typeof AdminFieldApp.fieldsCollection.find(function(model) { return "undefined" !== typeof AdminFieldApp.fieldsCollection.find(function(model) {
@@ -86,7 +97,7 @@ define([
$(".save-block", AdminFieldApp.$top).removeClass("loading"); $(".save-block", AdminFieldApp.$top).removeClass("loading");
} }
this._disableSaveButton(active); this.updateStateButton();
this._overlay(active); this._overlay(active);
} }
}); });

View File

@@ -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([ define([
"underscore", "underscore",
"backbone" "backbone"

View File

@@ -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([ define([
"underscore", "underscore",
"backbone" "backbone"

View File

@@ -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([ define([
"underscore", "underscore",
"backbone" "backbone"

View File

@@ -287,6 +287,14 @@ define([
AdminFieldApp.fieldListView.render(); 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() { it("should update edit view when clicking on single element", function() {
AdminFieldApp.fieldListView.itemViews[0].clickAction(); AdminFieldApp.fieldListView.itemViews[0].clickAction();
should.exist(AdminFieldApp.fieldEditView); should.exist(AdminFieldApp.fieldEditView);
@@ -296,15 +304,7 @@ define([
it("should reorder collection on drop action", function() { it("should reorder collection on drop action", function() {
var ui = {item: {index: function() {return 2;}}}; var ui = {item: {index: function() {return 2;}}};
AdminFieldApp.fieldListView.itemViews[0].dropAction({},ui); AdminFieldApp.fieldListView.itemViews[0].dropAction({},ui);
assert.equal(AdminFieldApp.fieldListView.collection.last().get('sorter'), 2, 'model is updated'); assert.equal(AdminFieldApp.fieldListView.collection.last().get('sorter'), 3, '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');
}); });
}); });
}); });