Add autocompleter for metatags

This commit is contained in:
Romain Neutron
2012-06-14 17:39:54 +02:00
parent e87c49d349
commit ace5166d12
4 changed files with 472 additions and 398 deletions

View File

@@ -32,6 +32,131 @@ class Description implements ControllerProviderInterface
$controllers = new ControllerCollection();
$controllers->get('/metadatas/search/', function(Application $app, Request $request) {
$term = trim(strtolower($request->get('term')));
$res = array();
if ($term) {
$provider = new \PHPExiftool\Driver\TagProvider();
$table = $provider->getLookupTable();
$table['phraseanet'] = array(
'pdftext' => array(
'tagname' => 'PdfText',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\PdfText',
'namespace' => 'Phraseanet'),
'tfarchivedate' => array(
'tagname' => 'TfArchivedate',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfArchivedate',
'namespace' => 'Phraseanet'
),
'tfatime' => array(
'tagname' => 'TfAtime',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfAtime',
'namespace' => 'Phraseanet'
),
'tfbits' => array(
'tagname' => 'TfBits',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfBits',
'namespace' => 'Phraseanet'
),
'tfbasename' => array(
'tagname' => 'TfBasename',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfBasename',
'namespace' => 'Phraseanet'),
'tfchannels' => array(
'tagname' => 'TfChannels',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfChannels',
'namespace' => 'Phraseanet'
),
'tTfCtime' => array(
'tagname' => 'TfCtime',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfCtime',
'namespace' => 'Phraseanet'
),
'tfduration' => array(
'tagname' => 'TfDuration',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfDuration',
'namespace' => 'Phraseanet'
),
'tfeditdate' => array(
'tagname' => 'TfEditdate',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfEditdate',
'namespace' => 'Phraseanet'
),
'tfextension' => array(
'tagname' => 'TfExtension',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfExtension',
'namespace' => 'Phraseanet'
),
'tffilename' => array(
'tagname' => 'TfFilename',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfFilename',
'namespace' => 'Phraseanet'
),
'tffilepath' => array(
'tagname' => 'TfFilepath',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfFilepath',
'namespace' => 'Phraseanet'
),
'tfheight' => array(
'tagname' => 'TfHeight',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfHeight',
'namespace' => 'Phraseanet'
),
'tfmimetype' => array(
'tagname' => 'TfMimetype',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfMimetype',
'namespace' => 'Phraseanet'
),
'tfmtime' => array(
'tagname' => 'TfMtime',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfMtime',
'namespace' => 'Phraseanet'
),
'tfdirname' => array(
'tagname' => 'TfDirname',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfDirname',
'namespace' => 'Phraseanet'
),
'tfrecordid' => array(
'tagname' => 'TfRecordid',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfRecordid',
'namespace' => 'Phraseanet'
),
'tfsize' => array(
'tagname' => 'TfSize',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfSize',
'namespace' => 'Phraseanet'
),
'tfwidth' => array(
'tagname' => 'TfWidth',
'classname' => '\\Alchemy\\Phrasea\\Metadata\\Tag\\TfWidth',
'namespace' => 'Phraseanet'
),
);
foreach ($table as $namespace => $tags) {
$ns = strpos($namespace, $term);
foreach ($tags as $tagname => $datas) {
if ($ns === false && strpos($tagname, $term) === false) {
continue;
}
$res[] = array(
'id' => $namespace . '/' . $tagname,
'label' => $datas['namespace'] . ' / ' . $datas['tagname'],
'value' => $datas['namespace'] . ':' . $datas['tagname'],
);
}
}
}
return new \Symfony\Component\HttpFoundation\JsonResponse($res);
});
$controllers->post('/{sbas_id}/', function(Application $app, $sbas_id) {
$Core = $app['Core'];
$user = $Core->getAuthenticatedUser();
@@ -44,7 +169,6 @@ class Description implements ControllerProviderInterface
$databox = \databox::get_instance((int) $sbas_id);
$fields = $databox->get_meta_structure();
$available_fields = \databox::get_available_metadatas();
$available_dc_fields = $databox->get_available_dcfields();
@@ -132,14 +256,12 @@ class Description implements ControllerProviderInterface
$databox = \databox::get_instance((int) $sbas_id);
$fields = $databox->get_meta_structure();
$available_fields = \databox::get_available_metadatas();
$available_dc_fields = $databox->get_available_dcfields();
$params = array(
'databox' => $databox,
'fields' => $fields,
'available_fields' => $available_fields,
'available_dc_fields' => $available_dc_fields,
'vocabularies' => \Alchemy\Phrasea\Vocabulary\Controller::getAvailable(),
);

View File

@@ -649,44 +649,6 @@ class databox extends base
return sprintf("%s@%s:%s (MySQL %s)", $this->dbname, $this->host, $this->port, $this->get_connection()->server_info());
}
/**
* Return an array of available metadata objects
*
* @return Array
*/
public static function get_available_metadatas()
{
$provider = new PHPExiftool\Driver\TagProvider();
$available = $provider->getAll();
$available['Phraseanet'] = array(
'PdfText' => new \Alchemy\Phrasea\Metadata\Tag\PdfText(),
'TfArchivedate' => new \Alchemy\Phrasea\Metadata\Tag\TfArchivedate(),
'TfAtime' => new \Alchemy\Phrasea\Metadata\Tag\TfAtime(),
'TfBits' => new \Alchemy\Phrasea\Metadata\Tag\TfBits(),
'TfBasename' => new \Alchemy\Phrasea\Metadata\Tag\TfBasename(),
'TfChannels' => new \Alchemy\Phrasea\Metadata\Tag\TfChannels(),
'TfCtime' => new \Alchemy\Phrasea\Metadata\Tag\TfCtime(),
'TfDuration' => new \Alchemy\Phrasea\Metadata\Tag\TfDuration(),
'TfEditdate' => new \Alchemy\Phrasea\Metadata\Tag\TfEditdate(),
'TfExtension' => new \Alchemy\Phrasea\Metadata\Tag\TfExtension(),
'TfFilename' => new \Alchemy\Phrasea\Metadata\Tag\TfFilename(),
'TfFilepath' => new \Alchemy\Phrasea\Metadata\Tag\TfFilepath(),
'TfHeight' => new \Alchemy\Phrasea\Metadata\Tag\TfHeight(),
'TfMimetype' => new \Alchemy\Phrasea\Metadata\Tag\TfMimetype(),
'TfMtime' => new \Alchemy\Phrasea\Metadata\Tag\TfMtime(),
'TfDirname' => new \Alchemy\Phrasea\Metadata\Tag\TfDirname(),
'TfRecordid' => new \Alchemy\Phrasea\Metadata\Tag\TfRecordid(),
'TfSize' => new \Alchemy\Phrasea\Metadata\Tag\TfSize(),
'TfWidth' => new \Alchemy\Phrasea\Metadata\Tag\TfWidth(),
);
ksort($available);
return $available;
}
public function get_available_dcfields()
{
return array(

View File

@@ -1,4 +1,3 @@
{% macro dces_selector(available_dc_sources, selected_field) %}
{% set disabled = '' %}
{% if selected_field.is_on_error() %}
@@ -35,13 +34,14 @@
}
.ui-autocomplete {
max-height: 200px;
max-width: 400px;
overflow-y: auto;
/* prevent horizontal scrollbar */
overflow-x: hidden;
/* add padding to account for vertical scrollbar */
padding-right: 20px;
}
/* IE 6 doesn't support max-height
/** IE 6 doesn't support max-height
* we use height instead, but this forces the menu to always be this tall
*/
* html .ui-autocomplete {
@@ -57,18 +57,31 @@
</style>
<script type="text/javascript">
$(document).ready(function(){
$( "#field_chooser" ).autocomplete({
source: "/admin/description/metadatas/search/",
minLength: 2,
select: function( event, ui ) {
$('#current_field_label').val(ui.item.value);
}
});
var buttons = {};
var dialog = $('#field_changer');
buttons['{% trans 'boutton::annuler' %}'] = function(){
$('#field_changer').dialog('close');
};
$('#tag_remover').bind('click', function(){
$('#current_field_label').val('');
});
buttons['{% trans 'boutton::valider' %}'] = function(){
var field_id = $('input[name="field_id"]', dialog).val();
$('#field_changer').dialog('close');
var selected_opt = $('#field_chooser option:selected');
var display = selected_opt.parent().attr('label')+' / '+selected_opt.html();
$('#display_value_'+field_id).empty().append(display);
$('input[name="src_'+field_id+'"]').val($('#field_chooser').val());
$('#display_value_'+field_id).empty().append($('#current_field_label').val());
$('input[name="src_'+field_id+'"]').val($('#current_field_label').val());
$('input[name="src_'+field_id+'"]').trigger('change');
$('.metafield_'+field_id).removeAttr('disabled');
};
@@ -78,8 +91,9 @@
}).dialog('close');
$('a.field_change').bind('click', function(){
$('#field_chooser').val('');
var field_id = $('input',this).val();
$('#field_chooser').val( $('input[name="src_'+field_id+'"]').val());
$('#current_field_label').val( $('input[name="src_'+field_id+'"]').val());
$('input[name="field_id"]', dialog).val(field_id);
dialog.dialog().dialog('open');
@@ -111,7 +125,6 @@
dialog_adder.dialog().dialog('open');
});
$('.meta_deleter').bind('click', function(){
if(confirm('{% trans 'Etes vous sur de vouloir supprimer cette metadonnee ? Elle sera definitivement perdue' %}'))
{
@@ -124,9 +137,9 @@
$('.dces_selector').bind('change', function(event){
if($.trim($(this).val()) === '')
if($.trim($(this).val()) === '') {
return;
}
var $this = $(this);
var $others = $('.dces_selector option[value="'+$this.val()+'"]:selected');
if($others.length > 1)
@@ -209,24 +222,10 @@
<div id="warning_dialog" style="display:none" title="{% trans 'Attention !' %}" >
</div>
<div style="display:none" id="field_changer">
<select id="field_chooser">
<option>{% trans 'choisir' %}</option>
{% set current_ns = '' %}
{% for group in available_fields %}
{% for field in group %}
{% if current_ns != field.getGroupName() %}
{% if current_ns != '' %}
</optgroup>
{% endif %}
{% set current_ns = field.getGroupName() %}
<optgroup label="{{field.getGroupName()}}">
{% endif %}
<option value="{{field.getTagname()}}">{{field.getTagname()}}</option>
{% endfor %}
{% endfor %}
</optgroup>
</select>
<input name="field_id" type="hidden"/>
<input id="field_chooser" type="text" value="">
<input type="text" readonly value="" id="current_field_label"/>
<button id="tag_remover">{% trans 'Delete' %}</button>
<input name="field_id" type="hidden" value=""/>
</div>
<div style="display:none" id="field_adder">
<input name="newfield_name" type="text"/>
@@ -242,53 +241,22 @@
<table class="admintable">
<thead>
<tr>
<th>
</th>
<th>
{% trans 'Nom' %}
</th>
<th>
{% trans 'Source' %}Source
</th>
<th>
<span title="{% trans 'DublinCore Element Set' %}">DCES</span>
</th>
<th>
{% trans 'Multivalue' %}
</th>
<th>
{% trans 'Indexable' %}
</th>
<th>
{% trans 'Vocabulary Type' %}
</th>
<th>
{% trans 'Vocabulary restricted' %}
</th>
<th>
{% trans 'Required' %}
</th>
<th>
{% trans 'Lecture seule' %}
</th>
<th>
{% trans 'Type' %}
</th>
<th>
{% trans 'Separateur' %}
</th>
<th>
{% trans 'Branche Thesaurus' %}
</th>
<th>
{% trans 'Affiche dans report' %}
</th>
<th>
{% trans 'Afficher en titre' %}
</th>
<th>
{% trans 'Business Field' %}
</th>
<th></th>
<th>{% trans 'Nom' %}</th>
<th>{% trans 'Source' %}Source</th>
<th><span title="{% trans 'DublinCore Element Set' %}">DCES</span></th>
<th>{% trans 'Multivalue' %}</th>
<th>{% trans 'Indexable' %}</th>
<th>{% trans 'Vocabulary Type' %}</th>
<th>{% trans 'Vocabulary restricted' %}</th>
<th>{% trans 'Required' %}</th>
<th>{% trans 'Lecture seule' %}</th>
<th>{% trans 'Type' %}</th>
<th>{% trans 'Separateur' %}</th>
<th>{% trans 'Branche Thesaurus' %}</th>
<th>{% trans 'Affiche dans report' %}</th>
<th>{% trans 'Afficher en titre' %}</th>
<th>{% trans 'Business Field' %}</th>
</tr>
</thead>
<tbody>
@@ -299,14 +267,17 @@
{% endif %}
<tr class="{% if loop.index is odd %}odd{% else %}even{% endif %}">
<td>
<a class="meta_deleter"><input type="hidden" value="{{field.get_id()}}"/><img src="/skins/icons/delete.png"></a>
<a class="meta_deleter">
<input type="hidden" value="{{ field.get_id() }}"/>
<img src="/skins/icons/delete.png">
</a>
<input type="hidden" name="field_ids[]" value="{{ field.get_id() }}" />
</td>
<td>
<input style="width:100px;" class="meta_namer metafield_{{ field.get_id() }}" {{ disabled }} type="text" name="name_{{ field.get_id() }}" value="{{ field.get_name () }}" />
</td>
<td>
<span id="display_value_{{field.get_id()}}">{{ field.get_tag().getGroupName() }} / {{ field.get_tag().getName() }}</span> <a href="#" class="field_change">change<input type="hidden" value="{{field.get_id()}}"/></a>
<span id="display_value_{{ field.get_id() }}">{{ field.get_tag().getGroupName() }}:{{ field.get_tag().getName() }}</span> <a href="#" class="field_change">change<input type="hidden" value="{{ field.get_id() }}"/></a>
<input class="meta_src" type="hidden" name="src_{{ field.get_id() }}" value="{{ field.get_tag().getTagname() }}"/>
</td>
<td>

View File

@@ -242,4 +242,23 @@ class DescriptionTest extends \PhraseanetWebTestCaseAuthenticatedAbstract
$this->client->request("GET", "/description/" . $databox->get_sbas_id() . "/");
$this->assertTrue($this->client->getResponse()->isOk());
}
public function testGetMetadatas()
{
$appbox = appbox::get_instance(\bootstrap::getCore());
$databox = array_shift($appbox->get_databoxes());
$this->client->request("GET", "/description/metadatas/search/", array('term'=>''));
$this->assertTrue($this->client->getResponse()->isOk());
$datas = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals(array(), $datas);
$this->client->request("GET", "/description/metadatas/search/", array('term'=>'xmp'));
$this->assertTrue($this->client->getResponse()->isOk());
$datas = json_decode($this->client->getResponse()->getContent(), true);
$this->assertTrue(is_array($datas));
$this->assertGreaterThan(0, count($datas));
}
}