Add admin field application

This commit is contained in:
Nicolas Le Goff
2013-05-12 19:51:29 +02:00
committed by Romain Neutron
parent 7199d38ab3
commit 05dc3659af
23 changed files with 1017 additions and 118 deletions

View File

@@ -17,6 +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;
class Fields implements ControllerProviderInterface class Fields implements ControllerProviderInterface
{ {
@@ -27,10 +28,18 @@ class Fields implements ControllerProviderInterface
$app['admin.fields.controller'] = $this; $app['admin.fields.controller'] = $this;
$controllers->before(function(Request $request) use ($app) { $controllers->before(function(Request $request) use ($app) {
$app['firewall']->requireAccessToModule('admin') $app['firewall']
->requireAccessToModule('admin')
->requireRight('bas_modify_struct'); ->requireRight('bas_modify_struct');
}); });
$controllers->get('/language.json', 'admin.fields.controller:getLanguage')
->bind('admin_fields_language');
$controllers->get('/{sbas_id}', 'admin.fields.controller:displayApp')
->assert('sbas_id', '\d+')
->bind('admin_fields');
$controllers->get('/{sbas_id}/fields', 'admin.fields.controller:listFields') $controllers->get('/{sbas_id}/fields', 'admin.fields.controller:listFields')
->assert('sbas_id', '\d+') ->assert('sbas_id', '\d+')
->bind('admin_fields_list'); ->bind('admin_fields_list');
@@ -75,9 +84,26 @@ class Fields implements ControllerProviderInterface
return $controllers; return $controllers;
} }
public function getLanguage(Application $app) {
return $app->json(array(
'something_wrong' => _('Something wrong happened, please try again or contact an admin if problem persists'),
'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'),
));
}
public function displayApp(Application $app, $sbas_id) {
return $app['twig']->render('/admin/fields/index.html.twig', array(
'sbas_id' => $sbas_id,
'js' => ''
));
}
public function listDcFields(Application $app, Request $request) public function listDcFields(Application $app, Request $request)
{ {
$data = $app['serializer']->serialize(\databox::get_available_dcfields(), 'json'); $data = $app['serializer']->serialize(array_values(\databox::get_available_dcfields()), 'json');
return new Response($data, 200, array('content-type' => 'application/json')); return new Response($data, 200, array('content-type' => 'application/json'));
} }
@@ -120,7 +146,7 @@ class Fields implements ControllerProviderInterface
continue; continue;
} }
$res[] = array( $res[$tagname] = array(
'id' => $namespace . '/' . $tagname, 'id' => $namespace . '/' . $tagname,
'label' => $datas['namespace'] . ' / ' . $datas['tagname'], 'label' => $datas['namespace'] . ' / ' . $datas['tagname'],
'value' => $datas['namespace'] . ':' . $datas['tagname'], 'value' => $datas['namespace'] . ':' . $datas['tagname'],
@@ -129,6 +155,8 @@ class Fields implements ControllerProviderInterface
} }
} }
ksort($res);
return $app->json($res); return $app->json($res);
} }
@@ -141,21 +169,40 @@ 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);
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();
return $app->json($field->toArray(), 201, array( $json['success'] = true;
'Location' => $app->path('admin_fields_show_field', array( $headers['location'] = $app->path('admin_fields_show_field', array(
'sbas_id' => $sbas_id, 'sbas_id' => $sbas_id,
'id' => $field->get_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']));
}
}
return $app->json($json, 201, $headers);
} }
public function listFields(Application $app, $sbas_id) { public function listFields(Application $app, $sbas_id) {

View File

@@ -0,0 +1,27 @@
{# empty Twig template #}
<script type="text/javascript" src="/assets/i18n/i18next-1.6.0.min.js"></script>
{% include 'admin/fields/templates.html.twig' %}
<div id="admin-field-app" class="container-fluid">
<input type="hidden" name="current_sbas_id" value="{{ sbas_id }}">
<div class="row-fluid" style="min-height:60px; border-bottom: 1px solid #000">
<div class="span4">
<button type="button" class="btn btn-large btn-success"><i class="icon-hdd icon-white"></i> {% trans %}Save all changes{% endtrans %}</button>
</div>
<div class="span8">
<div class="block-alert well-small"></div>
</div>
</div>
<div class="row-fluid">
<div class="left-block span4">
</div>
<div class="right-block span8" style="border-left: 1px dashed #000">
</div>
</div>
</div>
<script src="/assets/requirejs/require.js"></script>
<script src="/scripts/apps/admin/fields/main.js"></script>

View File

@@ -0,0 +1,201 @@
<script type="text/template" id="alert_template">
<button type="button" class="close" data-dismiss="alert">&times;</button>
<%= msg %>
</script>
<script type="text/template" id="modal_delete_confirm_template">
<div class="modal-body">
<p><%= msg %></p>
</div>
<div class="modal-footer">
<button type="button" data-dismiss="modal" class="btn cancel">{% trans %}Close{% endtrans %}</button>
<button type="button" class="btn btn-primary confirm">{% trans %}Ok{% endtrans %}</button>
</div>
</script>
<script type="text/template" id="item_list_view_template">
<div class="row-fluid">
<div class="span12">
<div style="margin:20px 0">
<button type="button" class="btn btn-success btn-add-field"><i class="icon-plus icon-white"></i>{% trans %}Add new field{% endtrans %}</button>
</div>
<div style="margin:20px 0">
<input class="input-block-level" type="text" id="live_search" placeholder="{% trans %}Live search{% endtrans %}"/>
</div>
<div class="well well-small add-field-block" style="display:none">
<h3>{% trans %}Add a new field{% endtrans %}</h3>
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="inputLabel">{% trans %}Label{% endtrans %}</label>
<div class="controls">
<input type="text" id="new-name" class="input-block-level" placeholder="">
<span class="help-block"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="inputSource">{% trans %}Source{% endtrans %}</label>
<div class="controls">
<input type="text" id="new-source" class="input-block-level" placeholder="">
</div>
</div>
<div class="control-group">
<div class="controls">
<label class="checkbox">
<input id="new-multivalued" type="checkbox">{% trans %}Multivalued{% endtrans %}
</label>
<button type="button" class="btn btn-success btn-submit-field"><i class="icon-ok icon-white"></i>{% trans %}Add{% endtrans %}</button>
<button type="button" class="btn btn-cancel-field">{% trans %}Cancel{% endtrans %}</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="row-fluid">
<div class="span12">
<ul id="collection-fields" class="unstyled" style="height:450px;overflow: auto;">
</ul>
</div>
</div>
</script>
<script type="text/template" id="edit_template">
<div style="padding: 5px 20px">
<table>
<tr style="height: 50px;">
<td>{% trans %}Order{% endtrans %}:</td>
<td><%= field.sorter %></td>
<td><button type="button" class="btn btn-danger delete-field pull-right"><i class="icon-trash icon-white"></i>delete</button></td>
</tr>
<tr style="height: 60px;">
<td colspan="2"><input id="name" style="font-size:28px;color:#0080FF;height:42px;line-height:42px; font-weight:bold" value="<%= field.name %>" class="input-block-level"></td>
</tr>
<tr>
<td>{% trans %}Source{% endtrans %} : </td>
<td><input id="tag" type="text" val="<%= field.tag %>" class="input-block-level"/></td>
</tr>
<tr>
<td>{% trans %}DCES{% endtrans %} : </td>
<td class="dc-fields-subview"></td>
</tr>
<tr>
<td colspan="3" class="dces-help-block"></td>
</tr>
<% if(field.multi == true) { %>
<tr>
<td colspan="2">
<i class='icon-ok'></i> {% trans %}Multivalued{% endtrans %}
</td>
</tr>
<% } %>
</table>
<div class="edit-form">
<h4 style="padding: 10px 0;">{% trans %}Advanced field parameter{% endtrans %}</h4>
<table>
<tr>
<td><label for="">{% trans %}Thesaurus branch{% endtrans %}</label></td>
<td><input id="tbranch" type="text" value="<%= field.tbranch %>"/></td>
</tr>
<tr>
<td><label for="">{% trans %}Vocabulary type{% endtrans %}</label></td>
<td>
<select id="vocabulary-type" class="input-block-level">
<% _.each(vocabularyTypes, function(vocab) { %>
<option value="<%= vocab.type %>"><%= vocab.name %></option>
<% }); %>
</select>
</td>
</tr>
<tr>
<td><label for="">{% trans %}Type{% endtrans %}</label></td>
<td>
<select id="type" class="input-block-level">
<option value=""></option>
<option <%= field.type == 'string' ? 'selected' : '' %> value="string">string</option>
<option <%= field.type == 'text' ? 'selected' : '' %> value="text">text</option>
<option <%= field.type == 'number' ? 'selected' : '' %> value="number">number</option>
<option <%= field.type == 'date' ? 'selected' : '' %> value="date">date</option>
</select>
</td>
</tr>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="business" type="checkbox" <%= field.business ? "checked='checked'" : "" %> />{% trans %}Business Fields{% endtrans %}</label></td>
</tr>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="vocabulary-restricted" type="checkbox" <%= field["vocavulary-restricted"] ? "checked='checked'" : "" %> />{% trans %}Limited vocabulary{% endtrans %}</label></td>
</tr>
<tr>
<td><label for="">{% trans %}Separator{% endtrans %}</label></td>
<td><input id="separator" type="text" value="<%= field.separator %>" /></td>
</tr>
</table>
<h4 style="padding: 10px 0;">{% trans %}display & action settings{% endtrans %}</h4>
<table>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="required" type="checkbox" <%= field.required ? "checked='checked'" : "" %> />{% trans %}Mandatory{% endtrans %}</label></td>
</tr>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="indexable" type="checkbox" <%= field.indexable ? "checked='checked'" : "" %> />{% trans %}Indexable{% endtrans %}</label></td>
</tr>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="readonly" type="checkbox" <%= field.readonly ? "checked='checked'" : "" %> />{% trans %}Read only{% endtrans %}</label></td>
</tr>
<tr>
<td colspan="2"><label for="" class="checkbox"><input id="report" type="checkbox" <%= field.report ? "checked='checked'" : "" %> />{% trans %}Report{% endtrans %}</label></td>
</tr>
<tr>
<td><label for="">{% trans %}Display thumbnails{% endtrans %}</label></td>
<td>
<select id="thumbtitle" class="input-block-level">
<option value="1" <%= field.thumbtitle == "1" ? "selected" : "" %> >{% trans 'Tous' %}</option>
<option value="0" <%= field.thumbtitle == "0" ? "selected" : "" %> >{% trans 'Aucun' %}</option>
<option value="fr" <%= field.thumbtitle == "fr" ? "selected" : "" %> >{% trans 'Francais' %}</option>
<option value="nl" <%= field.thumbtitle == "nl" ? "selected" : "" %> >{% trans 'Dutch' %}</option>
<option value="de" <%= field.thumbtitle == "de" ? "selected" : "" %> >{% trans 'Allemand' %}</option>
<option value="en" <%= field.thumbtitle == "en" ? "selected" : "" %> >{% trans 'Anglais' %}</option>
<option value="ar" <%= field.thumbtitle == " r" ? "selected" : "" %> >{% trans 'Arabe' %}</option>
</select>
</td>
</tr>
</table>
</div>
</div>
</script>
<script type="text/template" id="list_row_template">
<table style="table-layout:fixed;width:100%;">
<tr>
<td class="handle" style="width: 10%; vertical-align:middle;text-align:center;cursor: move;">
<i class="icon-move"></i>
</td>
<td rowspan="2" class="trigger-click" style="padding:10px">
<div class="field-name"><%= name %></div>
<div class="field-tag"><%= tag %></div>
</td>
<td rowspan="2" class="trigger-click" style="width: 10%;padding:10px;">
<i class="icon-chevron-right"></i>
</td>
</tr>
<tr>
<td class="position" style="width: 10%; vertical-align:bottom;text-align: center">
<%= position %>
</td>
</tr>
</table>
</script>
<script type="text/template" id="dc_fields_template">
<select id="dces-element" val="" class="input-block-level">
<% _.each(dces_elements, function(el) { %>
<option value="<%= el.label %>">DC:<%= el.label %></option>
<% }); %>
</select>
<div class="help-block"></div>
</script>

View File

@@ -8,7 +8,7 @@
{% endblock %} {% endblock %}
{% block stylesheet %} {% block stylesheet %}
<link type="text/css" rel="stylesheet" href="/include/minify/?f=include/jslibs/jquery-ui-1.8.17/css/ui-lightness/jquery-ui-1.8.17.custom.css,include/jslibs/jquery-treeview/jquery.treeview.css,include/jslibs/jquery.contextmenu.css,skins/account/geonames.css,skins/common/main.css,skins/admin/css/Main.css,skins/admin/css/Bases.css,skins/admin/css/Tables.css" /> <link type="text/css" rel="stylesheet" href="/include/minify/?f=include/jslibs/jquery-ui-1.8.17/css/ui-lightness/jquery-ui-1.8.17.custom.css,include/jslibs/jquery-treeview/jquery.treeview.css,include/jslibs/jquery.contextmenu.css,skins/account/geonames.css,skins/common/main.css,skins/admin/css/Main.css,skins/admin/css/Bases.css,skins/admin/css/Tables.css,skins/admin/css/fields.css " />
{% endblock %} {% endblock %}

View File

@@ -111,7 +111,7 @@
</a> </a>
</li> </li>
<li> <li>
<a target="right" href="/admin/description/{{ sbas_id }}/" class="ajax"> <a target="right" href="{{ path('admin_fields', {'sbas_id': sbas_id}) }}" class="ajax">
<img src="/skins/icons/miniadjust01.gif"/> <img src="/skins/icons/miniadjust01.gif"/>
<span>{% trans 'CHAMPS' %}</span> <span>{% trans 'CHAMPS' %}</span>
</a> </a>

View File

@@ -2,10 +2,48 @@ define([
'jquery', 'jquery',
'underscore', 'underscore',
'backbone', 'backbone',
'apps/admin/fields/router' 'i18n',
], function($, _, Backbone, Router) { 'apps/admin/fields/collections/fields',
'apps/admin/fields/collections/vocabularies',
'apps/admin/fields/collections/dcFields',
'apps/admin/fields/views/list'
], function($, _, Backbone, i18n, FieldsCollection, VocabulariesCollection, DcFieldsCollection, FieldListView) {
var initialize = function() { var initialize = function() {
Router.initialize(); window.AdminFieldApp = {};
window.AdminFieldApp.sbas_id = $('input[name=current_sbas_id]').val();
var fieldsCollection = new FieldsCollection(null, {
sbas_id : window.AdminFieldApp.sbas_id
});
var vocabulariesCollection = new VocabulariesCollection();
var dcFieldsCollection = new DcFieldsCollection();
// load strings synchronously
i18n.init({ resGetPath: '/admin/fields/language.json', getAsync: false });
var requests = [
fieldsCollection.fetch(),
vocabulariesCollection.fetch(),
dcFieldsCollection.fetch()
];
$.when.apply($, requests).done(
function() {
window.AdminFieldApp.vocabularyCollection = vocabulariesCollection;
window.AdminFieldApp.dcFieldsCollection = dcFieldsCollection;
window.AdminFieldApp.fieldListView = new FieldListView({
collection: fieldsCollection,
el: $('.left-block')[0]
});
window.AdminFieldApp.fieldListView.render();
// click on first item list
_.first(window.AdminFieldApp.fieldListView.itemViews).clickAction().animate();
}
);
}; };
return { return {

View File

@@ -0,0 +1,17 @@
define([
'underscore',
'backbone',
'models/dcField'
], function(_, Backbone, DcFieldModel) {
var DcFieldCollection = Backbone.Collection.extend({
model: DcFieldModel,
url: function() {
return '/admin/fields/dc-fields';
},
comparator: function(item) {
return item.get("label");
}
});
return DcFieldCollection;
});

View File

@@ -1,11 +1,58 @@
define([ define([
'underscore', 'underscore',
'backbone', 'backbone',
'models/admin/field' 'models/field'
], function(_, Backbone, FieldModel) { ], function(_, Backbone, FieldModel) {
var FieldCollection = Backbone.Collection.extend({ var FieldCollection = Backbone.Collection.extend({
initialize: function(models, options) {
if (!"sbas_id" in options) {
throw "You must specify a sbasId option when creating a new field model"
}
this.sbasId = options.sbas_id;
},
model: FieldModel, model: FieldModel,
url: '/admin/fields/1/fields' url: function() {
return '/admin/fields/' + this.sbasId + '/fields';
},
search: function(letters) {
if (letters === "")
return this;
var pattern = new RegExp(letters, "gi");
return _(this.filter(function(data) {
return pattern.test(data.get("name"));
}));
},
comparator: function(item) {
return item.get("sorter");
},
nextIndex: function(model) {
var index = this.indexOf(model);
if (index < 0) {
throw "Model not found"
}
if ((index + 1) === this.length) {
return null;
}
return index + 1;
},
previousIndex: function(model) {
var index = this.indexOf(model);
if (index < 0) {
throw "Model not found"
}
if (index === 0) {
return null;
}
return index - 1;
}
}); });
return FieldCollection; return FieldCollection;

View File

@@ -0,0 +1,17 @@
define([
'underscore',
'backbone',
'models/vocabulary'
], function(_, Backbone, VocabularyModel) {
var VocabularyCollection = Backbone.Collection.extend({
model: VocabularyModel,
url: function() {
return '/admin/fields/vocabularies';
},
comparator: function(item) {
return item.get("name");
}
});
return VocabularyCollection;
});

View File

@@ -1,15 +1,21 @@
require.config({ require.config({
baseUrl: "/scripts", baseUrl: "/scripts",
paths: { paths: {
jquery: '../assets/jquery/jquery', jquery: '../include/minify/f=include/jslibs/jquery-1.7.1',
jqueryui: '../include/jslibs/jquery-ui-1.8.17/js/jquery-ui-1.8.17.custom.min',
underscore: '../assets/underscore-amd/underscore', underscore: '../assets/underscore-amd/underscore',
backbone: '../assets/backbone-amd/backbone', backbone: '../assets/backbone-amd/backbone',
twig: '../assets/twig/twig', twig: '../assets/twig/twig',
i18n: '../assets/i18n/i18next.amd' i18n: '../assets/i18n/i18next.amd',
bootstrap: ['//netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/js/bootstrap.min']
}, },
shim: { shim: {
twig: { twig: {
exports: 'Twig' exports: 'Twig'
},
bootstrap : ['jquery'],
jqueryui: {
deps: [ 'jquery' ]
} }
} }
}); });

View File

@@ -1,59 +0,0 @@
define([
'jquery',
'underscore',
'backbone',
'models/admin/field',
'apps/admin/fields/views/edit',
'apps/admin/fields/views/list',
'apps/admin/fields/collections/fields'
], function($, _, Backbone, FieldModel, FieldEditView, FieldListView, FieldsCollection) {
var AppRouter = Backbone.Router.extend({
routes: {
'field/:id': "getField",
'fields': 'showFields',
'*actions': 'defaultAction'
}
});
var initialize = function() {
var app_router = new AppRouter();
app_router.on('route:getField', function (id) {
var field = new FieldModel({id: id});
field.fetch().done(function() {
var fieldEditView = new FieldEditView({
el: $('.right-block')[0],
model: field
});
fieldEditView.render();
});
});
app_router.on('route:showFields', function() {
var fieldsCollection = new FieldsCollection();
fieldsCollection.fetch().done(function() {
var fieldListView = new FieldListView({
collection: fieldsCollection,
el: $('ul#collection-fields')[0]
});
fieldListView.render();
});
});
app_router.on('route:defaultAction', function(actions) {
console.log('No route:', actions);
});
Backbone.history.start();
// Show fields on start up
app_router.navigate('fields', { trigger: true });
};
return {
initialize: initialize
};
});

View File

@@ -0,0 +1,51 @@
define([
'underscore',
'backbone',
'i18n'
], function( _, Backbone, i18n, bootstrap) {
var AddView = Backbone.View.extend({
tagName: "div",
className: "add-field-block",
style: "display:none;",
events: {
"click .btn-submit-field": "createAction"
},
initialize: function() {},
render: function() {
var template = _.template($("#alert_template").html(), {
msg: this.msg
});
this.$el.after(template);
return this;
},
createAction: function(event) {
var self = this;
var field = new FieldModel({
"name": "AA" + new Date().getUTCMilliseconds(),
"tag": "IPTC:ObjectName"
}, {
sbas_id: AdminFieldApp.sbas_id
});
field.save(null, {
success: function(field) {
self.collection.add(field);
_.last(self.itemViews).clickAction().animate();
new AlertView({alert: 'success', message: 'A new field has been created'}).render();
},
error: function() {
new AlertView({alert: 'error', message: 'Something wrong happened'}).render();
}
});
},
destroy: function() {
this.remove();
}
});
return AddView;
});

View File

@@ -0,0 +1,36 @@
define([
'underscore',
'backbone',
'i18n',
'bootstrap'
], function(_, Backbone, i18n, bootstrap) {
var AlertView = Backbone.View.extend({
tagName: "div",
className: "alert",
initialize: function(options) {
var self = this;
if (options) {
this.alert = options.alert || "info";
this.message = options.message || "";
}
// remove view when alert is closed
this.$el.bind('closed', function () {
self.remove();
});
},
render: function() {
var template = _.template($("#alert_template").html(), {
msg: this.message
});
this.$el.addClass("alert-" + this.alert).html(template).alert();
$('.block-alert').empty().append(this.$el);
return this;
}
});
return AlertView;
});

View File

@@ -0,0 +1,29 @@
define([
'underscore',
'backbone',
'i18n'
], function( _, Backbone, i18n, bootstrap) {
var DcFieldsView = Backbone.View.extend({
tagName: "div",
className: "input-append",
events: {
"change select": "onChange"
},
render: function() {
var template = _.template($("#dc_fields_template").html(), {
dces_elements: this.collection.toJSON()
});
this.$el.html(template);
return this;
},
onChange: function(e) {
var index = $(e.target)[0].selectedIndex;
this.$el.closest('table').find('.dces-help-block').empty().append(this.collection.at(index).get('definition'));
}
});
return DcFieldsView;
});

View File

@@ -1,19 +1,133 @@
define([ define([
'jquery',
'underscore', 'underscore',
'backbone', 'backbone',
'i18n' 'i18n',
], function($, _, Backbone, i18n) { 'apps/admin/fields/views/alert',
'apps/admin/fields/views/modal',
'apps/admin/fields/views/dcField',
], function(_, Backbone, i18n, AlertView, ModalView, DcFieldView) {
var FieldEditView = Backbone.View.extend({ var FieldEditView = Backbone.View.extend({
tagName: "div", tagName: "div",
className: "field-edit", className: "field-edit",
initialize: function() {
this.model.on('change', this.render, this);
this.model.on('change:name', this.onFieldChange, this);
this.model.on('change:tag', this.onFieldChange, this);
this.dcFieldsSubView = new DcFieldView({
collection: window.AdminFieldApp.dcFieldsCollection
});
},
render: function() { render: function() {
this.el.innerHTML = ''; var template = _.template($("#edit_template").html(), {
this.el.innerHTML = Twig.render(fieldsEdit, { field: this.model.toJSON(),
field: this.model.attributes vocabularyTypes: window.AdminFieldApp.vocabularyCollection.toJSON()
});
this.$el.empty().html(template);
this.assign({
'.dc-fields-subview' : this.dcFieldsSubView
});
$("#tag", this.$el).autocomplete({
source: function(request, response) {
$.ajax({
url: "/admin/fields/tags/search",
dataType: "json",
data: {
term: request.term
},
success: function(data) {
response($.map(data, function(item) {
return {
label: item.label,
value: item.value
};
}));
}
});
}
}).val(this.model.get('tag')).autocomplete("widget").addClass("ui-autocomplete-admin-field");
return this;
},
events: {
"click .delete-field": "deleteAction",
"focusout input[type=text]": "fieldChanged",
"change input[type=checkbox]": "fieldChanged",
"change select": "selectionChanged"
},
selectionChanged: function(e) {
var field = $(e.currentTarget);
var value = $("option:selected", field).val();
var data = {};
data[field.attr('id')] = value;
this.model.set(data);
},
fieldChanged: function(e) {
console.log('field change');
var field = $(e.currentTarget);
var data = {};
data[field.attr('id')] = field.is(":checkbox") ? field.is(":checked") : field.val();
this.model.set(data);
},
deleteAction: function() {
var self = this;
var modalView = new ModalView({
model: this.model,
message: i18n.t("are_you_sure_delete", { postProcess: "sprintf", sprintf: [this.model.get('name')] })
});
var previousIndex = AdminFieldApp.fieldListView.collection.previousIndex(this.model);
var nextIndex = AdminFieldApp.fieldListView.collection.nextIndex(this.model);
var itemView;
if (previousIndex) {
itemView = AdminFieldApp.fieldListView.itemViews[previousIndex];
} else if (nextIndex) {
itemView = AdminFieldApp.fieldListView.itemViews[nextIndex];
}
modalView.render();
modalView.on('modal:confirm', function() {
self.model.destroy({
success: function(model, response) {
AdminFieldApp.fieldListView.collection.remove(self.model);
if (itemView) {
itemView.clickAction().animate();
}
new AlertView({alert: 'info', message: i18n.t("deleted_success", { postProcess: "sprintf", sprintf: [model.get('name')] })}).render();
},
error: function(model, xhr) {
new AlertView({alert: 'error', message: i18n.t("something_wrong")}).render();
}
});
}); });
return this; return this;
},
onFieldChange: function() {
console.log('on field changed');
AdminFieldApp.fieldListView.collection.remove(this.model, {silent: true});
AdminFieldApp.fieldListView.collection.add(this.model);
this.render();
},
assign: function(selector, view) {
var selectors;
if (_.isObject(selector)) {
selectors = selector;
}
else {
selectors = {};
selectors[selector] = view;
}
if (!selectors)
return;
_.each(selectors, function(view, selector) {
view.setElement(this.$(selector)).render();
}, this);
} }
}); });

View File

@@ -1,30 +1,145 @@
define([ define([
'jquery', 'jqueryui',
'underscore', 'underscore',
'backbone', 'backbone',
'apps/admin/fields/views/listRow' 'i18n',
], function($, _, Backbone, FieldListRowView) { 'apps/admin/fields/views/listRow',
'apps/admin/fields/views/alert',
'models/field'
], function(jqueryui, _, Backbone, i18n, FieldListRowView, AlertView, FieldModel) {
var FieldListView = Backbone.View.extend({ var FieldListView = Backbone.View.extend({
events: {
"keyup #live_search": "searchAction",
"click .btn-submit-field": "createAction",
"click .btn-add-field": "toggleCreateFormAction",
"click .btn-cancel-field": "toggleCreateFormAction",
"update-sort": "onUpdateSort"
},
initialize: function() { initialize: function() {
var that = this; // Store all single rendered views
this._fieldViews = []; this.itemViews = [];
this.collection.each(function(field) { _.bindAll(this, "render");
that._fieldViews.push(new FieldListRowView({ // rerender whenever there is a change on the collection
model: field this.collection.bind("reset", this.render, this);
})); this.collection.bind("add", this.render, this);
}); this.collection.bind("remove", this.render, this);
}, },
render: function() { render: function() {
var that = this; var template = _.template($("#item_list_view_template").html(), {});
$(this.el).empty();
// Render each sub-view and append it to the parent view's element. this.$el.empty().html(template);
_(this._fieldViews).each(function(singleView) {
$(that.el).append(singleView.render().el); this.$listEl = $("ul#collection-fields", this.$el);
this._renderList(this.collection);
$("#new-source", this.$el).autocomplete({
source: function(request, response) {
$.ajax({
url: "/admin/fields/tags/search",
dataType: "json",
data: {
term: request.term
},
success: function(data) {
response($.map(data, function(item) {
return {
label: item.label,
value: item.value
};
}));
}
}); });
}
}).autocomplete("widget").addClass("ui-autocomplete-admin-field");
return this; return this;
},
_renderList: function(fields) {
var that = this;
this.$listEl.empty();
this.itemViews = [];
fields.each(function(field) {
var singleView = new FieldListRowView({
model: field,
id: 'field-' + field.get('id')
});
that.$listEl.append(singleView.render().el);
that.itemViews.push(singleView);
});
this.$listEl.sortable({
handle: ".handle",
start: function () {
console.log('start', AdminFieldApp.fieldEditView.model.get('id'));
},
stop: function(event, ui) {
ui.item.trigger('drop', ui.item.index());
}
});
this.$listEl.disableSelection();
this.$listEl.find('li:last').addClass('last');
return this;
},
searchAction: function(event) {
this._renderList(this.collection.search($("#live_search", this.$el).val()));
},
createAction: function(event) {
var self = this;
var fieldName = $("#new-name", this.$el);
if ('' == fieldName.val()) {
fieldName.closest('.control-group').addClass('error').find('.help-block').empty().append(i18n.t('validation_blank'));
return;
}
var field = new FieldModel({
"sbas-id": AdminFieldApp.sbas_id,
"name": fieldName.val(),
"tag": $("#new-source", this.$el).val(),
"multi": $("#new-multivalued", this.$el).is(':checked')
});
field.save(null, {
success: function(field, response, options) {
if (response.success) {
self.collection.add(field);
_.last(self.itemViews).clickAction().animate();
new AlertView({alert: 'success', message: response.message }).render();
} else {
new AlertView({alert: 'warning', message: response.message}).render();
}
},
error: function(model, xhr, options) {
new AlertView({alert: 'error', message: i18n.t("something_wrong")}).render();
self.toggleCreateFormAction();
}
});
},
toggleCreateFormAction: function(event) {
$('.add-field-block', this.$el).toggle();
},
onUpdateSort: function(event, model, position) {
this.collection.remove(model, {silent: true});
this.collection.each(function(model, index) {
var ordinal = index;
if (index >= position) ordinal += 1;
model.set('sorter', ordinal);
});
model.set('sorter', position);
this.collection.add(model, {at: position});
// update edit view
AdminFieldApp.fieldEditView.model = this.collection.find(function(el) { return el.get('id') === AdminFieldApp.fieldEditView.model.get('id') });
} }
}); });

View File

@@ -1,19 +1,67 @@
define([ define([
'jquery',
'underscore', 'underscore',
'backbone' 'backbone',
], function($, _, Backbone) { 'apps/admin/fields/views/edit',
'apps/admin/fields/views/alert'
], function(_, Backbone, FieldEditView, AlertView) {
var FieldListRowView = Backbone.View.extend({ var FieldListRowView = Backbone.View.extend({
tagName: "li", tagName: "li",
className: "field-row", className: "field-row",
initialize: function() {
// destroy view is model is deleted
this.model.on('destroy', this.remove, this);
},
events : {
"click .trigger-click": "clickAction",
"drop" : "dropAction"
},
clickAction: function (e) {
this.select();
// first click create view else update model's view
if (typeof AdminFieldApp.fieldEditView === 'undefined') {
AdminFieldApp.fieldEditView = new FieldEditView({
el: $('.right-block')[0],
model: this.model
});
} else {
AdminFieldApp.fieldEditView.model = this.model;
}
AdminFieldApp.fieldEditView.render();
return this;
},
dropAction: function(event, index) {
this.$el.trigger('update-sort', [this.model, index]);
},
render: function() { render: function() {
this.el.innerHTML = Twig.render(fieldsRow, { var template = _.template($("#list_row_template").html(), {
id: this.model.get('id'), id: this.model.get('id'),
position: this.model.get('sorter'),
name: this.model.get('name'), name: this.model.get('name'),
tag: this.model.get('tag') tag: this.model.get('tag')
}); });
this.$el.empty().html(template);
if (AdminFieldApp.fieldEditView && AdminFieldApp.fieldEditView.model.get('id') === this.model.get('id')) {
this.select();
}
return this; return this;
},
// set selected class
select: function () {
$("li", this.$el.closest('ul')).removeClass('selected');
this.$el.addClass('selected');
return this;
},
animate: function () {
var offset = this.$el.offset();
this.$el.closest('div').animate({
scrollTop: offset.top - 20
});
} }
}); });

View File

@@ -0,0 +1,44 @@
define([
'underscore',
'backbone',
'i18n',
'bootstrap'
], function(_, Backbone, i18n, bootstrap) {
var ModalView = Backbone.View.extend({
tagName: "div",
className: "modal",
events: {
'click .confirm': 'confirmAction'
},
initialize: function (options) {
var self = this;
// remove view when modal is closed
this.$el.on('hidden', function() {
self.remove();
});
if (options) {
this.message = options.message;
}
},
render: function() {
var template = _.template($("#modal_delete_confirm_template").html(), {
msg: this.message || ''
});
this.$el.html(template).modal();
return this;
},
confirmAction: function () {
this.trigger('modal:confirm');
this.$el.modal('hide');
this.remove();
return this;
}
});
return ModalView;
});

View File

@@ -1,11 +0,0 @@
define([
'underscore',
'backbone'
], function(_, Backbone) {
var FieldModel = Backbone.Model.extend({
urlRoot: '/admin/fields/1/fields'
});
// Return the model for the module
return FieldModel;
});

View File

@@ -0,0 +1,13 @@
define([
'underscore',
'backbone'
], function(_, Backbone) {
var DcFieldModel = Backbone.Model.extend({
urlRoot: function () {
return '/admin/fields/dc-fields';
}
});
// Return the model for the module
return DcFieldModel;
});

View File

@@ -0,0 +1,33 @@
define([
'underscore',
'backbone'
], function(_, Backbone) {
var FieldModel = Backbone.Model.extend({
initialize : function(attributes, options) {
if (attributes && ! "sbas-id" in attributes) {
throw "You must set a sbas id";
}
},
urlRoot: function () {
return '/admin/fields/'+ this.get('sbas-id') +'/fields';
},
defaults: {
"business": false,
"type": "string",
"thumbtitle": "0",
"tbranch": "",
"separator": "",
"required": false,
"report": true,
"readonly": false,
"multi": false,
"indexable": true,
"dces-element": null,
"vocabulary-type": null,
"vocabulary-restricted": false
}
});
// Return the model for the module
return FieldModel;
});

View File

@@ -0,0 +1,13 @@
define([
'underscore',
'backbone'
], function(_, Backbone) {
var VocabularyModel = Backbone.Model.extend({
urlRoot: function () {
return '/admin/fields/vocabularies';
}
});
// Return the model for the module
return VocabularyModel;
});

View File

@@ -0,0 +1,73 @@
#admin-field-app li {
background: #FFF;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-right: 1px solid #ccc;
}
#admin-field-app li .field-name {
font-weight: bold;
font-size: 16px;
color: #666;
}
#admin-field-app li .handle, #admin-field-app li .position {
color: #ccc;
border-right: 1px solid #ccc;
}
#admin-field-app li.last {
border-bottom: 1px solid #ccc;
}
#admin-field-app li.selected {
border-top-color: #0080FF;
background: #FFF;
color: #000;
}
#admin-field-app li.selected + li {
border-top-color: #0080FF;
}
#admin-field-app li.selected .field-name {
color: #0080FF;
}
#admin-field-app .add-field-block .control-label {
width: 80px;
text-align: left;
}
#admin-field-app .add-field-block .controls {
margin-left: 100px;
}
#admin-field-app .edit-form label {
width: 160px;
}
#admin-field-app .edit-form select {
min-width: 220px;
}
.ui-autocomplete-admin-field {
list-style-type: none;
overflow-y: scroll;
height: 180px;
background: #FFF;
max-width: 300px;
-webkit-box-shadow: 0 10px 6px -6px #777;
-moz-box-shadow: 0 10px 6px -6px #777;
box-shadow: 0 10px 6px -6px #777;
}
.ui-autocomplete-admin-field li{
padding: 3px;
}
.ui-autocomplete-admin-field li a {
text-decoration: none;
}