[SearchEngine] Add support for queries by date

This commit is contained in:
Romain Neutron
2012-10-26 20:09:05 +02:00
parent 8a829f03b1
commit c5e9b1f7b5
30 changed files with 804 additions and 189 deletions

View File

@@ -23,6 +23,7 @@ use Alchemy\Phrasea\Core\Provider\GeonamesServiceProvider;
use Alchemy\Phrasea\Core\Provider\ORMServiceProvider;
use Alchemy\Phrasea\Core\Provider\SearchEngineServiceProvider;
use Alchemy\Phrasea\Core\Provider\TaskManagerServiceProvider;
use Alchemy\Phrasea\Core\Provider\UnicodeServiceProvider;
use FFMpeg\FFMpegServiceProvider;
use Grom\Silex\ImagineServiceProvider;
use MediaVorus\MediaVorusServiceProvider;
@@ -135,6 +136,7 @@ class Application extends SilexApplication
$this->register(new TaskManagerServiceProvider());
$this->register(new UnoconvServiceProvider());
$this->register(new UrlGeneratorServiceProvider());
$this->register(new UnicodeServiceProvider());
$this->register(new ValidatorServiceProvider());
$this->register(new XPDFServiceProvider());

View File

@@ -24,7 +24,7 @@ use Alchemy\Phrasea\Controller\Admin\Fields;
use Alchemy\Phrasea\Controller\Admin\Publications;
use Alchemy\Phrasea\Controller\Admin\Root;
use Alchemy\Phrasea\Controller\Admin\Setup;
use Alchemy\Phrasea\Controller\Admin\Sphinx;
use Alchemy\Phrasea\Controller\Admin\SearchEngine;
use Alchemy\Phrasea\Controller\Admin\Subdefs;
use Alchemy\Phrasea\Controller\Admin\TaskManager;
use Alchemy\Phrasea\Controller\Admin\Users;
@@ -123,7 +123,7 @@ return call_user_func(function($environment = null) {
$app->mount('/admin/databox', new Databox());
$app->mount('/admin/databoxes', new Databoxes());
$app->mount('/admin/setup', new Setup());
$app->mount('/admin/sphinx', new Sphinx());
$app->mount('/admin/search-engine', new SearchEngine());
$app->mount('/admin/connected-users', new ConnectedUsers());
$app->mount('/admin/publications', new Publications());
$app->mount('/admin/users', new Users());

View File

@@ -83,7 +83,7 @@ class Setup implements ControllerProviderInterface
*/
public function getGlobals(Application $app, Request $request)
{
require_once __DIR__ . "/../../../../conf.d/_GV_template.inc";
$GV = require_once __DIR__ . "/../../../../conf.d/_GV_template.inc";
if (null !== $update = $request->query->get('update')) {
if (!!$update) {
@@ -110,10 +110,14 @@ class Setup implements ControllerProviderInterface
public function postGlobals(Application $app, Request $request)
{
if (\setup::create_global_values($app, $request->request->all())) {
return $app->redirect('/admin/globals/?success=1');
return $app->redirect($app['url_generator']->generate('setup_display_globals', array(
'success' => 1
)));
}
return $app->redirect('/admin/globals/?success=0');
return $app->redirect($app['url_generator']->generate('setup_display_globals', array(
'success' => 0
)));
}
/**

View File

@@ -11,7 +11,8 @@
namespace Alchemy\Phrasea\Controller\Prod;
use Silex\Application;
use Alchemy\Phrasea\Application;
use Silex\Application as SilexApplication;
use Silex\ControllerProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Finder\Finder;
@@ -25,7 +26,7 @@ use Alchemy\Phrasea\Helper;
class Root implements ControllerProviderInterface
{
public function connect(Application $app)
public function connect(SilexApplication $app)
{
$controllers = $app['controllers_factory'];
@@ -122,7 +123,7 @@ class Root implements ControllerProviderInterface
'thesau_js_list' => $thjslist,
'thesau_json_sbas' => json_encode($sbas),
'thesau_json_bas2sbas' => json_encode($bas2sbas),
'thesau_languages' => \User_Adapter::avLanguages(),
'thesau_languages' => $app->getAvailableLanguages(),
));
});

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2012 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Alchemy\Phrasea\Core\Provider;
use Silex\Application;
use Silex\ServiceProviderInterface;
class UnicodeServiceProvider implements ServiceProviderInterface
{
public function register(Application $app)
{
$app['unicode'] = $app->share(function($app) {
return new \unicode();
});
}
public function boot(Application $app)
{
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Alchemy\Phrasea\SearchEngine;
abstract class AbstractConfigurationPanel implements ConfigurationPanelInterface
{
abstract public function getName();
public function getConfigPathFile()
{
return __DIR__ . '/../../../../config/'.$this->getName().'.json';
}
public function getAvailableDateFields($databoxes)
{
$date_fields = array();
foreach ($databoxes as $databox) {
foreach($databox->get_meta_structure() as $field) {
if ($field->get_type() !== \databox_field::TYPE_DATE) {
continue;
}
$date_fields[] = $field->get_name();
}
}
return $date_fields;
}
}

View File

@@ -3,10 +3,10 @@
namespace Alchemy\Phrasea\SearchEngine\Phrasea;
use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\SearchEngine\ConfigurationPanelInterface;
use Alchemy\Phrasea\SearchEngine\AbstractConfigurationPanel;
use Symfony\Component\HttpFoundation\Request;
class ConfigurationPanel implements ConfigurationPanelInterface
class ConfigurationPanel extends AbstractConfigurationPanel
{
protected $charsets;
protected $searchEngine;
@@ -16,13 +16,57 @@ class ConfigurationPanel implements ConfigurationPanelInterface
$this->searchEngine = $engine;
}
public function getName()
{
return 'phrasea-engine';
}
public function get(Application $app, Request $request)
{
return $app['twig']->render('admin/search-engine/phrasea.html.twig', array());
$configuration = $this->getConfiguration();
$params = array(
'configuration' => $configuration,
'date_fields' => $this->getAvailableDateFields($app['phraseanet.appbox']->get_databoxes()),
'available_sort'=> $this->searchEngine->getAvailableSort(),
);
return $app['twig']->render('admin/search-engine/phrasea.html.twig', $params);
}
public function post(Application $app, Request $request)
{
$configuration = $this->getConfiguration();
$configuration['date_fields'] = array();
foreach ($request->request->get('date_fields', array()) as $field) {
$configuration['date_fields'][] = $field;
}
$configuration['default_sort'] = $request->request->get('default_sort');
file_put_contents($this->getConfigPathFile(), json_encode($configuration));
return $app->redirect($app['url_generator']->generate('admin_searchengine_get'));
}
public function getConfiguration()
{
$configuration = @json_decode(file_get_contents(__DIR__ . '/../../../../../config/phrasea-engine.json'), true);
if (!is_array($configuration)) {
$configuration = array();
}
if (!isset($configuration['date_fields'])) {
$configuration['date_fields'] = array();
}
if (!isset($configuration['default_sort'])) {
$configuration['default_sort'] = null;
}
return $configuration;
}
}

View File

@@ -21,12 +21,15 @@ use Doctrine\Common\Collections\ArrayCollection;
class PhraseaEngine implements SearchEngineInterface
{
private $initialized;
/**
*
* @var SearchEngineOptions
*/
private $options;
private $app;
private $dateFields;
private $configuration;
private $queries = array();
private $arrayq = array();
private $colls = array();
@@ -44,6 +47,67 @@ class PhraseaEngine implements SearchEngineInterface
$this->options = new SearchEngineOptions();
}
public function getAvailableDateFields()
{
if (!$this->dateFields) {
foreach ($this->app['phraseanet.appbox']->get_databoxes() as $databox) {
foreach ($databox->get_meta_structure() as $databox_field) {
if ($databox_field->get_type() != \databox_field::TYPE_DATE) {
continue;
}
$this->dateFields[] = $databox_field->get_name();
}
}
$this->dateFields = array_unique($this->dateFields);
}
return $this->dateFields;
}
public function getConfiguration()
{
if (!$this->configuration) {
$this->configuration = $this->configurationPanel()->getConfiguration();
}
return $this->configuration;
}
public function getDefaultSort()
{
$configuration = $this->getConfiguration();
return $configuration['default_sort'];
}
public function getAvailableSort()
{
$date_fields = $this->getAvailableDateFields();
$sort = array('' => _('No sort'));
foreach ($date_fields as $field) {
$sort[$field] = $field;
}
return $sort;
}
public function getAvailableOrder()
{
return array(
'desc' => _('descendant'),
'asc' => _('ascendant'),
);
}
public function hasStemming()
{
return false;
}
public function initialize()
{
if ($this->initialized) {
@@ -621,4 +685,5 @@ class PhraseaEngine implements SearchEngineInterface
return $this;
}
}

View File

@@ -32,6 +32,16 @@ interface SearchEngineInterface
public function configurationPanel();
public function getAvailableDateFields();
public function getAvailableSort();
public function getDefaultSort();
public function getAvailableOrder();
public function hasStemming();
/**
*
* @return an array of self::GEM_TYPE_* indexed types

View File

@@ -2,13 +2,14 @@
namespace Alchemy\Phrasea\SearchEngine\SphinxSearch;
use Alchemy\Phrasea\SearchEngine\ConfigurationPanelInterface;
use Alchemy\Phrasea\SearchEngine\AbstractConfigurationPanel;
use Alchemy\Phrasea\Application;
use Symfony\Component\Finder\Finder;
use Symfony\Component\HttpFoundation\Request;
class ConfigurationPanel implements ConfigurationPanelInterface
class ConfigurationPanel extends AbstractConfigurationPanel
{
const DATE_FIELD_PREFIX = 'date_field_';
protected $charsets;
protected $searchEngine;
@@ -17,6 +18,11 @@ class ConfigurationPanel implements ConfigurationPanelInterface
$this->searchEngine = $engine;
}
public function getName()
{
return 'sphinx-search';
}
public function get(Application $app, Request $request)
{
$configuration = $this->getConfiguration();
@@ -25,40 +31,77 @@ class ConfigurationPanel implements ConfigurationPanelInterface
'configuration' => $configuration,
'configfile' => $this->generateSphinxConf($app['phraseanet.appbox']->get_databoxes(), $configuration),
'charsets' => $this->get_available_charsets(),
'date_fields' => $this->getAvailableDateFields($app['phraseanet.appbox']->get_databoxes()),
);
return $app['twig']->render('admin/search-engine/sphinx-search.html.twig', $params);
}
public function getConfiguration()
{
$configuration = @json_decode(file_get_contents(__DIR__ . '/../../../../../config/sphinx-search.json'), true);
if ( ! is_array($configuration)) {
$configuration = array();
}
if ( ! isset($configuration['charset_tables'])) {
$configuration['charset_tables'] = array("common","latin");
}
return $configuration;
}
public function post(Application $app, Request $request)
{
$configuration = $this->getConfiguration();
$configuration['charset_tables'] = array();
$configuration['date_fields'] = array();
foreach ($request->request->get('charset_tables', array()) as $table) {
$configuration['charset_tables'][] = $table;
}
foreach ($request->request->get('date_fields', array()) as $field) {
$configuration['date_fields'][] = $field;
}
file_put_contents(__DIR__ . '/../../../../../config/sphinx-search.json', json_encode($configuration));
$configuration['host'] = $request->request->get('host');
$configuration['host'] = $request->request->get('port');
$configuration['rt_host'] = $request->request->get('rt_host');
$configuration['rt_port'] = $request->request->get('rt_port');
$this->saveConfiguration($configuration);
return $app->redirect($app['url_generator']->generate('admin_searchengine_get'));
}
public function getConfiguration()
{
$configuration = @json_decode(file_get_contents($this->getConfigPathFile()), true);
if (!is_array($configuration)) {
$configuration = array();
}
if (!isset($configuration['charset_tables'])) {
$configuration['charset_tables'] = array("common", "latin");
}
if (!isset($configuration['date_fields'])) {
$configuration['date_fields'] = array();
}
if (!isset($configuration['host'])) {
$configuration['host'] = '127.0.0.1';
}
if (!isset($configuration['port'])) {
$configuration['port'] = 9306;
}
if (!isset($configuration['rt_host'])) {
$configuration['rt_host'] = '127.0.0.1';
}
if (!isset($configuration['rt_port'])) {
$configuration['rt_port'] = 9308;
}
return $configuration;
}
public function saveConfiguration($configuration)
{
file_put_contents($this->getConfigPathFile(), json_encode($configuration));
return $this;
}
public function get_available_charsets()
{
if (null !== $this->charsets) {
@@ -107,7 +150,7 @@ class ConfigurationPanel implements ConfigurationPanelInterface
$charsets = explode("\n", $charsets);
$last_detect = false;
for ($i = (count($charsets) - 1); $i >= 0; $i -- ) {
for ($i = (count($charsets) - 1); $i >= 0; $i--) {
if (trim($charsets[$i]) === '') {
unset($charsets[$i]);
continue;
@@ -156,6 +199,20 @@ class ConfigurationPanel implements ConfigurationPanelInterface
$index_crc = $this->searchEngine->CRCdatabox($databox);
$date_selects = $date_left_joins = $date_fields = array();
foreach ($configuration['date_fields'] as $name) {
$field = $databox->get_meta_structure()->get_element_by_name($name);
$date_fields[] = self::DATE_FIELD_PREFIX . $name;
if ($field instanceof \databox_field) {
$date_selects[] = ", UNIX_TIMESTAMP(d" . $field->get_id() . ".value) as " . self::DATE_FIELD_PREFIX . $name;
$date_left_joins[] = " LEFT JOIN metadatas d" . $field->get_id() . " ON (d" . $field->get_id() . ".record_id = r.record_id AND d" . $field->get_id() . ".meta_struct_id = " . $field->get_id() . ")";
} else {
$date_selects[] = ", null as " . $name;
}
}
$conf .= '
@@ -219,7 +276,9 @@ class ConfigurationPanel implements ConfigurationPanelInterface
UNIX_TIMESTAMP(credate) as created_on, 0 as deleted, \
CRC32(CONCAT_WS("_", r.coll_id, s.business)) as crc_coll_business, \
s.business \
FROM metadatas m, metadatas_structure s, record r \
' . implode(" \\\n", $date_selects) . ' \
FROM (metadatas m, metadatas_structure s, record r) \
' . implode(" \\\n", $date_left_joins) . ' \
WHERE m.record_id = r.record_id AND m.meta_struct_id = s.id \
AND s.indexable = "1"
@@ -236,6 +295,12 @@ class ConfigurationPanel implements ConfigurationPanelInterface
sql_attr_uint = business
sql_attr_uint = crc_coll_business
sql_attr_timestamp = created_on
';
foreach ($date_fields as $date_field) {
$conf.= " sql_attr_timestamp = $date_field\n";
}
$conf .= '
sql_attr_multi = uint status from query; SELECT m.id as id, \
CRC32(CONCAT_WS("_", ' . $databox->get_sbas_id() . ', s.name)) as name \
@@ -329,7 +394,13 @@ class ConfigurationPanel implements ConfigurationPanelInterface
rt_attr_uint = business
rt_attr_uint = crc_coll_business
rt_attr_timestamp = created_on
rt_attr_multi = status
';
foreach ($date_fields as $date_field) {
$conf.= " rt_attr_timestamp = $date_field\n";
}
$conf .= ' rt_attr_multi = status
}
#--------------------------------------
@@ -338,13 +409,16 @@ class ConfigurationPanel implements ConfigurationPanelInterface
source src_documents' . $index_crc . ' : database_cfg' . $index_crc . '
{
sql_query = \
SELECT r.record_id as id, record_id, r.parent_record_id, ' . $databox->get_sbas_id() . ' as sbas_id, \
SELECT r.record_id as id, r.record_id, r.parent_record_id, ' . $databox->get_sbas_id() . ' as sbas_id, \
CRC32(CONCAT_WS("_", ' . $databox->get_sbas_id() . ', r.coll_id)) as crc_sbas_coll, \
CRC32(CONCAT_WS("_", ' . $databox->get_sbas_id() . ', r.record_id)) as crc_sbas_record, \
CONCAT_WS("_", ' . $databox->get_sbas_id() . ' , r.coll_id) as sbas_coll, \
CRC32(r.type) as crc_type, r.coll_id, \
UNIX_TIMESTAMP(credate) as created_on, 0 as deleted \
FROM record r
UNIX_TIMESTAMP(r.credate) as created_on, 0 as deleted \
' . implode(" \\\n", $date_selects) . ' \
FROM (record r) \
' . implode(" \\\n", $date_left_joins) . ' \
WHERE 1
# documents can be filtered / sorted on each sql_attr
sql_attr_uint = record_id
@@ -356,6 +430,12 @@ class ConfigurationPanel implements ConfigurationPanelInterface
sql_attr_uint = crc_type
sql_attr_uint = deleted
sql_attr_timestamp = created_on
';
foreach ($date_fields as $date_field) {
$conf.= " sql_attr_timestamp = $date_field\n";
}
$conf .= '
sql_attr_multi = uint status from query; SELECT r.record_id as id, \
CRC32(CONCAT_WS("_", ' . $databox->get_sbas_id() . ', s.name)) as name \
@@ -443,7 +523,13 @@ class ConfigurationPanel implements ConfigurationPanelInterface
rt_attr_uint = crc_type
rt_attr_uint = deleted
rt_attr_timestamp = created_on
rt_attr_multi = status
';
foreach ($date_fields as $date_field) {
$conf.= " rt_attr_timestamp = $date_field\n";
}
$conf .= ' rt_attr_multi = status
}
#------------------------------------------------------------------------------

View File

@@ -40,6 +40,8 @@ class SphinxSearchEngine implements SearchEngineInterface
* @var \PDO
*/
protected $rt_conn;
private $dateFields;
protected $configuration;
protected $configurationPanel;
protected $options;
protected $app;
@@ -69,6 +71,52 @@ class SphinxSearchEngine implements SearchEngineInterface
return $this;
}
public function getAvailableDateFields()
{
if (!$this->dateFields) {
foreach ($this->app['phraseanet.appbox']->get_databoxes() as $databox) {
foreach ($databox->get_meta_structure() as $databox_field) {
if ($databox_field->get_type() != \databox_field::TYPE_DATE) {
continue;
}
$this->dateFields[] = $databox_field->get_name();
}
}
$this->dateFields = array_unique($this->dateFields);
}
return $this->dateFields;
}
public function getDefaultSort()
{
return 'relevance';
}
public function getAvailableSort()
{
return array(
'relevance' => _('pertinence'),
'created_on' => _('date dajout'),
'random' => _('aleatoire'),
);
}
public function getAvailableOrder()
{
return array(
'desc' => _('descendant'),
'asc' => _('ascendant'),
);
}
public function hasStemming()
{
return true;
}
public function status()
{
if (false === $this->sphinx->Status()) {
@@ -99,6 +147,15 @@ class SphinxSearchEngine implements SearchEngineInterface
return $this->configurationPanel;
}
public function getConfiguration()
{
if (!$this->configuration) {
$this->configuration = $this->configurationPanel()->getConfiguration();
}
return $this->configuration;
}
public function availableTypes()
{
return array(self::GEM_TYPE_RECORD, self::GEM_TYPE_STORY);
@@ -121,6 +178,8 @@ class SphinxSearchEngine implements SearchEngineInterface
}
}
$sql_date_fields = $this->getSqlDateFields($record);
foreach ($record->get_caption()->get_fields(null, true) as $field) {
if (!$field->is_indexable()) {
continue;
@@ -148,7 +207,9 @@ class SphinxSearchEngine implements SearchEngineInterface
," . (int) $value->getDatabox_field()->isBusiness() . "
," . sprintf("%u", crc32($record->get_collection()->get_coll_id() . '_' . (int) $value->getDatabox_field()->isBusiness())) . "
," . $record->get_creation_date()->format('U') . "
,(" . implode(',', $status) . ") )");
" . $sql_date_fields . "
,(" . implode(',', $status) . ")
)");
}
}
@@ -165,13 +226,40 @@ class SphinxSearchEngine implements SearchEngineInterface
," . sprintf("%u", crc32($record->get_type())) . "
,0
," . $record->get_creation_date()->format('U') . "
,(" . implode(',', $status) . ") )";
" . $sql_date_fields . "
,(" . implode(',', $status) . ")
)";
$this->rt_conn->exec($sql);
return $this;
}
private function getSqlDateFields(\record_adapter $record)
{
$configuration = $this->getConfiguration();
$sql_fields = array();
foreach ($configuration['date_fields'] as $field_name) {
try {
$value = $record->get_caption()->get_field($field_name)->get_serialized_values();
} catch (\Exception $e) {
$value = null;
}
if ($value) {
$date = \DateTime::createFromFormat('Y/m/d H:i:s', $this->app['unicode']->parseDate($value));
$value = $date->format('U');
}
$sql_fields[] = $value ? : '-1';
}
return ($sql_fields ? ', ' : '') . implode(',', $sql_fields);
}
public function removeRecord(\record_adapter $record)
{
if (!$this->rt_conn) {
@@ -419,6 +507,17 @@ class SphinxSearchEngine implements SearchEngineInterface
$this->sphinx->SetFilter('deleted', array(0));
$this->sphinx->SetFilter('parent_record_id', array($options->searchType()));
if ($options->getDateFields() && ($options->getMaxDate() || $options->getMinDate())) {
foreach (array_unique(array_map(function(\databox_field $field) {
return $field->get_name();
}, $options->getDateFields())) as $field) {
$min = $options->getMinDate() ? $options->getMinDate()->format('U') : 0;
$max = $options->getMaxDate() ? $options->getMaxDate()->format('U') : pow(2, 32);
$this->sphinx->SetFilterRange(ConfigurationPanel::DATE_FIELD_PREFIX . $field, $min, $max);
}
}
if ($options->fields()) {
@@ -829,5 +928,6 @@ class SphinxSearchEngine implements SearchEngineInterface
{
return $this;
}
}

View File

@@ -79,8 +79,9 @@ class Installer
$this->app['phraseanet.registry']->set($key, $value, \registry::TYPE_STRING);
}
// required to load GV template
$app = $this->app;
require __DIR__ . '/../../../../lib/conf.d/_GV_template.inc';
$GV = require __DIR__ . '/../../../../lib/conf.d/_GV_template.inc';
foreach ($GV as $section) {
foreach ($section['vars'] as $var) {

View File

@@ -29,13 +29,11 @@ class Migration31 implements MigrationInterface
throw new \LogicException('Required config files not found');
}
/**
* Required for GV :/
*/
// required to load GV template
$app = $this->app;
require __DIR__ . '/../../../../../../config/_GV.php';
require __DIR__ . '/../../../../../../lib/conf.d/_GV_template.inc';
$GV = require __DIR__ . '/../../../../../../lib/conf.d/_GV_template.inc';
$retrieve_old_credentials = function() {
require __DIR__ . '/../../../../../../config/connexion.inc';
@@ -61,11 +59,6 @@ class Migration31 implements MigrationInterface
character_set_server = 'utf8'");
define('GV_STATIC_URL', '');
define('GV_sphinx', false);
define('GV_sphinx_host', '');
define('GV_sphinx_port', '');
define('GV_sphinx_rt_host', '');
define('GV_sphinx_rt_port', '');
$connection->exec("CREATE TABLE IF NOT EXISTS `registry` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,

View File

@@ -406,6 +406,9 @@ class API_V1_adapter extends API_V1_Abstract
'defaultQuery' => $app['phraseanet.registry']->get('GV_defaultQuery'),
'defaultQueryType' => $app['phraseanet.registry']->get('GV_defaultQuery_type'),
),
/**
* @todo neutron update this
*/
'sphinx' => array(
'active' => $app['phraseanet.registry']->get('GV_sphinx'),
'host' => $app['phraseanet.registry']->get('GV_sphinx_host'),

View File

@@ -1380,29 +1380,6 @@ class User_Adapter implements User_Interface, cache_cacheableInterface
return $this;
}
public static function avLanguages()
{
$lngs = array();
$path = __DIR__ . "/../../../locale";
if ($hdir = opendir($path)) {
while (false !== ($file = readdir($hdir))) {
if (substr($file, 0, 1) == "." || strtolower($file) == "cvs")
continue;
if (is_dir($path . "/" . $file) && strpos($file, '_') == 2 && strlen($file) == 5) {
if (!array_key_exists($file, self::$locales))
continue;
$supFile = explode('_', $file);
if (!isset($lngs[$supFile[0]]))
$lngs[$supFile[0]] = array();
$lngs[$supFile[0]][$file] = array('name' => self::$locales[$file], 'selected' => false);
}
}
}
return $lngs;
}
public static function get_wrong_email_users(Application $app)
{

View File

@@ -146,8 +146,6 @@ interface User_Interface
public function get_nonce();
public static function avLanguages();
public function setPrefs($prop, $value);
public function getPrefs($prop);

View File

@@ -1294,11 +1294,12 @@ class databox extends base
$missing_locale = array();
$avLanguages = User_Adapter::avLanguages();
foreach ($avLanguages as $lang)
foreach ($lang as $k => $v)
if ( ! isset($TOU[$k]))
$missing_locale[] = $k;
$avLanguages = $this->app->getAvailableLanguages();
foreach ($avLanguages as $code=>$language) {
if ( ! isset($TOU[$code])) {
$missing_locale[] = $code;
}
}
$date_obj = new DateTime();
$date = $this->app['date-formatter']->format_mysql($date_obj);

View File

@@ -0,0 +1,111 @@
<?php
/*
* This file is part of Phraseanet
*
* (c) 2005-2012 Alchemy
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Alchemy\Phrasea\Application;
/**
*
* @license http://opensource.org/licenses/gpl-3.0 GPLv3
* @link www.phraseanet.com
*/
class patch_3803 implements patchInterface
{
/**
*
* @var string
*/
private $release = '3.8.0.a3';
/**
*
* @var Array
*/
private $concern = array(base::APPLICATION_BOX);
/**
*
* @return string
*/
public function get_release()
{
return $this->release;
}
public function require_all_upgrades()
{
return false;
}
/**
*
* @return Array
*/
public function concern()
{
return $this->concern;
}
/**
* @param base $appbox
*/
public function apply(base $appbox, Application $app)
{
$searchEngine = $app['phraseanet.registry']->get('GV_sphinx') ? 'sphinx-search' : 'phrasea';
$app['phraseanet.registry']->set('GV_search_engine', $searchEngine, \registry::TYPE_ENUM);
$phraseaConfiguration = null;
$phraseaConfigFile = __DIR__ . '/../../../config/phrasea-engine.json';
if (file_exists($phraseaConfigFile)) {
$phraseaConfiguration = json_decode(file_get_contents($phraseaConfigFile), true);
}
if (!is_array($phraseaConfiguration)) {
$phraseaConfiguration = array();
}
if ($app['phraseanet.registry']->get('GV_phrasea_sort')) {
$phraseaConfiguration['default_sort'] = $app['phraseanet.registry']->get('GV_phrasea_sort');
}
file_put_contents($phraseaConfigFile, $phraseaConfiguration);
$sphinxConfiguration = null;
$sphinxConfigFile = __DIR__ . '/../../../config/sphinx-search.json';
if (file_exists($sphinxConfigFile)) {
$sphinxConfiguration = json_decode(file_get_contents($sphinxConfigFile), true);
}
if (!is_array($sphinxConfiguration)) {
$sphinxConfiguration = array();
}
if ($app['phraseanet.registry']->get('GV_sphinx_rt_port')) {
$sphinxConfiguration['rt_port'] = $app['phraseanet.registry']->get('GV_sphinx_rt_port');
}
if ($app['phraseanet.registry']->get('GV_sphinx_rt_host')) {
$sphinxConfiguration['rt_host'] = $app['phraseanet.registry']->get('GV_sphinx_rt_host');
}
if ($app['phraseanet.registry']->get('GV_sphinx_port')) {
$sphinxConfiguration['port'] = $app['phraseanet.registry']->get('GV_sphinx_port');
}
if ($app['phraseanet.registry']->get('GV_sphinx_host')) {
$sphinxConfiguration['host'] = $app['phraseanet.registry']->get('GV_sphinx_host');
}
file_put_contents($sphinxConfigFile, $sphinxConfiguration);
return;
}
}

View File

@@ -75,7 +75,7 @@ class setup
public static function create_global_values(Application $app, $datas = array())
{
require(__DIR__ . "/../../lib/conf.d/_GV_template.inc");
$GV = require(__DIR__ . "/../../lib/conf.d/_GV_template.inc");
$debug = $log_errors = false;
$vars = array();

View File

@@ -119,7 +119,6 @@
if(dest && dest.indexOf('#') !== 0) {
$('#right-ajax').empty().addClass('loading').parent().show();
$('#right').hide();
$.get(dest, function(data) {
$('#right-ajax').removeClass('loading').html(data);
@@ -145,10 +144,7 @@
var dest = $(this).attr('href');
$(this).bind('click',function(){
if($(this).hasClass('ajax'))
{
$('#right-ajax').empty().addClass('loading').parent().show();
$('#right').hide();
$.get(dest, function(data) {
$('#right-ajax').removeClass('loading').html(data);
@@ -158,12 +154,6 @@
enableLink($(el));
});
});
}
else
{
$('#right-ajax').empty().parent().hide();
$('#right').show().addClass('loading').attr('src',dest);
}
$('#tree .selected').removeClass('selected');
$(this).parent().addClass('selected');
@@ -191,8 +181,6 @@
function resize()
{
$('#right').height($(this).height()-$('#mainMenu').height()-20);
$('#right').width($('#mainContent').width()-$('#left').width()-20);
bodySize.y = $(window).height() - $('#mainMenu').outerHeight();
bodySize.x = $(window).width();
}
@@ -236,7 +224,6 @@
</div>
{% endif %}
<iframe class="right PNB10" src="about:blank;" name="right" id="right" frameborder="1" border="0" framespacing="0" style="left:260px;border:none;right:0;{{ notice ? "top:30px" : "top:0" }}"></iframe>
<div class="right PNB" style="left:250px;overflow:auto;">
<div id="right-ajax" class="PNB10"></div>
</div>

View File

@@ -0,0 +1,16 @@
<h1>{% trans 'Phrasea search-engine configuration' %}</h1>
<form method="post" action="{{ path('admin_searchengine_post') }}">
<div>{% trans 'Date fields available for search' %}</div>
{% for field in date_fields %}
<input type="checkbox" name="date_fields[]" value="{{ field }}" {% if field in configuration['date_fields'] %}checked="checked"{% endif %} > {{ field }}
{% endfor %}
<div>{% trans 'Default sort' %}</div>
<select name="default_sort">
{% for sort, sort_name in available_sort %}
<option value="{{ sort }}" {%if configuration['default_sort'] == sort %}selected="selected"{% endif %}>{{ sort_name }}</option>
{% endfor %}
</select>
<button type="submit">{% trans 'boutton::valider' %}</button>
</form>

View File

@@ -1,9 +1,26 @@
<form method="post">
<h1>{% trans 'SphinxSearch search-engine configuration' %}</h1>
<form method="post" action="{{ path('admin_searchengine_post') }}">
<div>{% trans 'Sphinx Search connection configuration' %}</div>
<div>{% trans 'Sphinx Search server' %}</div>
<input type="text" name="host" value="{{ configuration['host'] | default('127.0.0.1') }}"/>
<input type="text" name="port" value="{{ configuration['port'] | default('93') }}"/>
<div>{% trans 'Sphinx Search RealTime server' %}</div>
<input type="text" name="rt_host" value="{{ configuration['rt_host'] }}"/>
<input type="text" name="rt_port" value="{{ configuration['rt_port'] }}"/>
<div>{% trans 'Charset to use for indexation' %}</div>
{% for charset, charsetObject in charsets %}
<input type="checkbox" name="charset_tables[]" value="{{ charset }}" {% if charset in configuration['charset_tables'] %}checked="checked"{% endif %} > {{ charsetObject.get_name() }}
{% endfor %}
<br/><br/>
<div>{% trans 'Date fields available for search' %}</div>
{% for field in date_fields %}
<input type="checkbox" name="date_fields[]" value="{{ field }}" {% if field in configuration['date_fields'] %}checked="checked"{% endif %} > {{ field }}
{% endfor %}
<button type="submit">{% trans 'boutton::valider' %}</button>
</form>
<textarea style="width:100%;height:70%">{{ configfile }}</textarea>
<textarea style="font-family: monospace;width:90%;height:70%">{{ configfile }}</textarea>

View File

@@ -340,32 +340,20 @@
<div class="btn-toolbar">
<input class="btn btn-inverse" type="button" value="{% trans 'Re-initialiser' %}" onclick="reset_adv_search();" />
</div>
{% if app['phraseanet.registry'].get('GV_sphinx') %}
<span>{% trans 'Trier par ' %}</span>
<select name="sort" class="input-mini">
<option value="relevance">{% trans 'pertinence'%}</option>
<option value="created_on">{% trans 'date dajout'%}</option>
<option value="random">{% trans 'aleatoire'%}</option>
</select>
<select name="ord" class="span2">
<option value="desc">{% trans 'descendant'%}</option>
<option value="asc">{% trans 'ascendant'%}</option>
</select>
<input type="checkbox" checked="checked" name="stemme" /> {% trans 'rechercher par stemme' %}
{% else %}
<span>{% trans 'Trier par ' %}</span>
<select name="sort" class="input-mini">
<option value=""></option>
{% for field_id, field in search_datas['fields'] %}
{% if field['type'] == 'date' %}
<option class="field_switch field_{{field['sbas']|join(' field_')}}" value="{{field_id}}" {% if app['phraseanet.registry'].get('GV_phrasea_sort') == field['fieldname'] %}selected="selected"{% endif %}>{{ field['fieldname'] }}</option>
{% endif %}
{% for sort, sort_name in app['phraseanet.SE'].getAvailableSort() %}
<option value="{{ sort }}" {% if sort == app['phraseanet.SE'].getDefaultSort() %}selected="selected"{% endif %}>{{ sort_name }}</option>
{% endfor %}
</select>
<select name="ord" class="span2">
<option value="desc">{% trans 'descendant'%}</option>
<option value="asc">{% trans 'ascendant'%}</option>
{% for ord, ord_name in app['phraseanet.SE'].getAvailableOrder() %}
<option value="{{ ord }}">{{ ord_name }}</option>
{% endfor %}
</select>
{% if app['phraseanet.SE'].hasStemming() %}
<input type="checkbox" checked="checked" name="stemme" /> {% trans 'rechercher par stemme' %}
{% endif %}
<div class="field_filter">
<span>{% trans 'Les termes apparaissent dans le(s) champs' %}</span>
@@ -417,8 +405,7 @@
</div>
{% endif %}
{% set dates_length = search_datas['dates']|length %}
{% if dates_length > 0 %}
{% if app['phraseanet.SE'].getAvailableDateFields() | length > 0 %}
<hr />
<div class="date_filter">
<span>{% trans 'Rechercher dans un champ date' %}</span>
@@ -426,11 +413,11 @@
<tr>
<td colspan="2">
<select name="datefield" class="input-medium">
{% for field_id, field in search_datas['dates'] %}
{% for field in app['phraseanet.SE'].getAvailableDateFields() %}
<option onchange="checkFilters(true);"
class="field_switch field_{{field['sbas']|join(' field_')}}" value="{{ field['fieldname'] }}">{{field['fieldname']}}</option>
class="" value="{{ field }}">{{ field }}</option>
{% endfor %}
<option value="{{search_datas['dates']|keys|join('|')}}" selected="selected">
<option value="{{configuration['date_fields']|keys|join('|')}}" selected="selected">
{% trans 'rechercher dans tous les champs' %}
</option>
</select>

View File

@@ -8,6 +8,8 @@ require_once __DIR__ . '/../../../PhraseanetPHPUnitAuthenticatedAbstract.class.i
abstract class SearchEngineAbstractTest extends \PhraseanetPHPUnitAuthenticatedAbstract
{
protected $options;
protected static $searchEngine;
protected static $initialized = false;
@@ -66,9 +68,19 @@ abstract class SearchEngineAbstractTest extends \PhraseanetPHPUnitAuthenticatedA
$options = new SearchEngineOptions();
$options->onCollections($databox->get_collections());
$this->options = $options;
self::$searchEngine->setOptions($options);
}
/**
* @return SearchEngineOptions
*/
private function getOptions()
{
return $this->options;
}
abstract public function initialize();
protected function updateIndex()
@@ -114,22 +126,141 @@ abstract class SearchEngineAbstractTest extends \PhraseanetPHPUnitAuthenticatedA
public function testQueryByDateMin()
{
$this->markTestSkipped('No yet implemented');
$record = self::$DI['record_24'];
$query_string = 'boomboklot' . $record->get_record_id() . 'dateMin';
$this->editRecord($query_string, $record);
$date_field = $this->editDateRecord('2012-12-21 12:12:00', $record);
if (!$date_field) {
$this->markTestSkipped('unable to add a date to record');
}
self::$searchEngine->addRecord($record);
$this->updateIndex();
$options = $this->getOptions();
$options->setDateFields(array($date_field));
$options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-23 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(0, $results->total());
$options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(1, $results->total());
}
private function editDateRecord($date, \record_adapter $record)
{
$date_field = null;
foreach ($record->get_databox()->get_meta_structure() as $databox_field) {
if ($databox_field->get_type() != \databox_field::TYPE_DATE) {
continue;
}
$date_field = $databox_field;
break;
}
if ($date_field) {
$toupdate = array();
try {
$values = $record->get_caption()->get_field($databox_field->get_name())->get_values();
$value = array_pop($values);
$meta_id = $value->getId();
} catch (\Exception $e) {
$meta_id = null;
}
$toupdate[$databox_field->get_id()] = array(
'meta_id' => $meta_id
, 'meta_struct_id' => $databox_field->get_id()
, 'value' => $date
);
$record->set_metadatas($toupdate);
}
return $date_field;
}
public function testQueryByDateMax()
{
$this->markTestSkipped('No yet implemented');
$record = self::$DI['record_24'];
$query_string = 'boomboklot' . $record->get_record_id() . 'dateMax';
$this->editRecord($query_string, $record);
$date_field = $this->editDateRecord('2012-12-21 12:12:00', $record);
if (!$date_field) {
$this->markTestSkipped('unable to add a date to record');
}
self::$searchEngine->addRecord($record);
$this->updateIndex();
$options = $this->getOptions();
$options->setDateFields(array($date_field));
$options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(0, $results->total());
$options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-23 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(1, $results->total());
}
public function testQueryByDateRange()
{
$this->markTestSkipped('No yet implemented');
$record = self::$DI['record_24'];
$query_string = 'boomboklot' . $record->get_record_id() . 'dateRange';
$this->editRecord($query_string, $record);
$date_field = $this->editDateRecord('2012-12-21 12:12:00', $record);
if (!$date_field) {
$this->markTestSkipped('unable to add a date to record');
}
public function testQueryInField()
{
$this->markTestSkipped('No yet implemented');
self::$searchEngine->addRecord($record);
$this->updateIndex();
$options = $this->getOptions();
$options->setDateFields(array($date_field));
$options->setMinDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-18 01:01:00'));
$options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-20 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(0, $results->total());
$options->setMaxDate(\DateTime::createFromFormat('Y-m-d H:i:s', '2012-12-22 01:01:00'));
self::$searchEngine->setOptions($options);
self::$searchEngine->resetCache();
$results = self::$searchEngine->query($query_string, 0, 1);
$this->assertEquals(1, $results->total());
}
protected function editRecord($string2add, \record_adapter &$record, $indexable = true, $business = false)

View File

@@ -23,7 +23,23 @@ class SphinxSearchEngineTest extends SearchEngineAbstractTest
self::$searchEngine = new SphinxSearchEngine($app, '127.0.0.1', 19306, '127.0.0.1', 19308);
self::$config = tempnam(sys_get_temp_dir(), 'tmp_sphinx.conf');
$configFile = self::$searchEngine->configurationPanel()->generateSphinxConf($appbox->get_databoxes(), self::$searchEngine->configurationPanel()->getConfiguration());
$configuration = self::$searchEngine->configurationPanel()->getConfiguration();
$configuration['date_fields'] = array();
foreach($appbox->get_databoxes() as $databox) {
foreach ($databox->get_meta_structure() as $databox_field) {
if ($databox_field->get_type() != \databox_field::TYPE_DATE) {
continue;
}
$configuration['date_fields'][] = $databox_field->get_name();
}
}
$configuration['date_fields'] = array_unique($configuration['date_fields']);
self::$searchEngine->configurationPanel()->saveConfiguration($configuration);
$configFile = self::$searchEngine->configurationPanel()->generateSphinxConf($appbox->get_databoxes(), $configuration);
file_put_contents(self::$config, $configFile);

View File

@@ -53,9 +53,11 @@ $obr = explode(';', $parm['obr']);
$t_lng = array();
if ($parm['ofm'] == 'tofiles') {
$t = User_Adapter::avLanguages();
foreach ($t as $lng_code => $lng)
foreach (Application::getAvailableLanguages() as $lng_code => $lng) {
$lng_code = explode('_', $lng_code);
$lng_code = $lng_code[0];
$t_lng[] = $lng_code;
}
} else {
$t_lng[] = $parm['piv'];
}

View File

@@ -129,8 +129,9 @@ if ($nbases > 0) {
}
$nf = 0;
$tlng = User_Adapter::avLanguages();
foreach ($tlng as $lng_code => $lng) {
foreach (Application::getAvailableLanguages() as $lng_code => $lng) {
$lng_code = explode('_', $lng_code);
$lng_code = $lng_code[0];
printf("<tr><td>%s</td>", $nf == 0 ? p4string::MakeString(_('thesaurus:: langue pivot')) /* Langue pivot : */ : "");
print("<td style=\"text-align:left\"><input type='radio' onclick=\"ckok();return(true);\" value='$lng_code' name='piv'><img src='/skins/lng/" . $lng_code . "_flag_18.gif' />&nbsp;(" . $lng_code . ")</td></tr>\n");
$nf ++;

View File

@@ -97,8 +97,9 @@ switch ($parm["typ"]) {
<td></td>
<td valign="bottom">
<?php
$tlng = User_Adapter::avLanguages();
foreach ($tlng as $lng_code => $lng) {
foreach (Application::getAvailableLanguages() as $lng_code => $lng) {
$lng_code = explode('_', $lng_code);
$lng_code = $lng_code[0];
$ck = $lng_code == $parm["piv"] ? " checked" : "";
?>
<span style="display:inline-block">

View File

@@ -78,8 +78,9 @@ if ($parm["dlg"]) {
<div class="menu" id="flagsMenu" style="z-index:50">
<?php
// on liste tous les drapeaux
$tlng = User_Adapter::avLanguages();
foreach ($tlng as $lng_code => $lng) {
foreach ($app->getAvailableLanguages() as $lng_code => $lng) {
$lng_code = explode('_', $lng_code);
$lng_code = $lng_code[0];
print("<a id='flagMenu_$lng_code' href='javascript:void(0)' class=''><img src='/skins/lng/" . $lng_code . "_flag_18.gif' />$lng_code</a>");
}
?>

View File

@@ -190,8 +190,9 @@ User_Adapter::updateClientInfos($app, 5);
<?php
// on liste tous les drapeaux
$jsFlags = "";
$tlng = User_Adapter::avLanguages();
foreach ($tlng as $lng_code => $lng) {
foreach (Application::getAvailableLanguages() as $lng_code => $lng) {
$lng_code = explode('_', $lng_code);
$lng_code = $lng_code[0];
if (file_exists("/skins/lng/" . $lng_code . "_flag_18.gif") && ($s = getimagesize("/skins/lng/" . $lng_code . "_flag_18.gif") )) {
printf("\t<img id='flagMenu_%s' src='/skins/lng/%s_flag_18.gif' />\n", $lng_code, $lng_code);