Merge branch '3.8'

Conflicts:
	.travis.yml
	templates/web/setup/wrapper.html.twig
	tests/Alchemy/Tests/Phrasea/Authentication/AuthenticatorTest.php
This commit is contained in:
Romain Neutron
2013-11-05 15:20:35 +01:00
19 changed files with 113 additions and 41 deletions

View File

@@ -66,4 +66,4 @@ return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) {
$app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_END, new ApiLoadEndEvent()); $app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_END, new ApiLoadEndEvent());
return $app; return $app;
}, isset($environment) ? $environment : null); }, isset($environment) ? $environment : PhraseaApplication::ENV_PROD);

View File

@@ -68,4 +68,4 @@ return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) {
); );
return $app; return $app;
}, isset($environment) ? $environment : null); }, isset($environment) ? $environment : PhraseaApplication::ENV_PROD);

View File

@@ -149,6 +149,19 @@ class Authenticator
*/ */
public function isAuthenticated() public function isAuthenticated()
{ {
return $this->session->has('usr_id'); if (!$this->session->has('usr_id')) {
return false;
}
if ($this->session->has('session_id')) {
if (null !== $this->em->find('Alchemy\Phrasea\Model\Entities\Session', $this->session->get('session_id'))) {
return true;
}
}
$this->session->invalidate();
$this->reinitUser();
return false;
} }
} }

View File

@@ -45,6 +45,10 @@ class Factory
case 'memcachecache': case 'memcachecache':
$cache = $this->createMemcache($options); $cache = $this->createMemcache($options);
break; break;
case 'memcached':
case 'memcachecached':
$cache = $this->createMemcached($options);
break;
case 'redis': case 'redis':
case 'rediscache': case 'rediscache':
$cache = $this->createRedis($options); $cache = $this->createRedis($options);
@@ -131,6 +135,29 @@ class Factory
return $cache; return $cache;
} }
private function createMemcached($options)
{
if (!extension_loaded('memcached')) {
throw new RuntimeException('The Memcached cache requires the Memcached extension.');
}
$host = isset($options['host']) ? $options['host'] : 'localhost';
$port = isset($options['port']) ? $options['port'] : 11211;
$memcached = new \Memcached();
$memcached->addServer($host, $port);
$memcached->getStats();
if (\Memcached::RES_SUCCESS !== $memcached->getResultCode()) {
throw new RuntimeException(sprintf("Memcached instance with host '%s' and port '%s' is not reachable", $host, $port));
}
$cache = new MemcachedCache();
$cache->setMemcached($memcached);
return $cache;
}
private function createApc($options) private function createApc($options)
{ {
if (!extension_loaded('apc')) { if (!extension_loaded('apc')) {

View File

@@ -113,8 +113,7 @@ class Installer
private function createUser($email, $password) private function createUser($email, $password)
{ {
$user = \User_Adapter::create($this->app, $email, $password, $email, true); $user = \User_Adapter::create($this->app, $email, $password, $email, true);
$this->app['authentication']->openAccount($user);
$this->app['session']->set('usr_id', $user->get_id());
return $user; return $user;
} }

View File

@@ -235,6 +235,8 @@ class databox_field implements cache_cacheableInterface
$this->thumbtitle = $row['thumbtitle']; $this->thumbtitle = $row['thumbtitle'];
$this->loadVocabulary();
return $this; return $this;
} }

View File

@@ -1,8 +1,8 @@
<td> <td>
<form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}"> <form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}">
<button class="form_multiple_submitter single_menu btn btn-inverse"> <a class="form_multiple_submitter single_menu btn btn-inverse">
{% trans 'Creer' %} {% trans 'Creer' %}
</button> </a>
</form> </form>
</td> </td>
<td> <td>

View File

@@ -1,8 +1,8 @@
<td> <td>
<form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}"> <form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}">
<button class="form_multiple_submitter single_menu btn btn-inverse"> <a class="form_multiple_submitter single_menu btn btn-inverse">
{% trans 'Creer' %} {% trans 'Creer' %}
</button> </a>
</form> </form>
</td> </td>
<td> <td>

View File

@@ -1,8 +1,8 @@
<td> <td>
<form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}"> <form class="action_works_standalone action_works_single_element action_works_many_element" method="GET" action="{{ path('bridge_account_action', { 'account_id' : account.get_id(), 'action' : 'createcontainer', 'element_type' : action_type }) }}">
<button class="form_multiple_submitter single_menu btn btn-inverse"> <a class="form_multiple_submitter single_menu btn btn-inverse">
{% trans 'Creer' %} {% trans 'Creer' %}
</button> </a>
</form> </form>
</td> </td>
<td> <td>

View File

@@ -2,7 +2,7 @@
"http://www.w3.org/TR/html4/strict.dtd"> "http://www.w3.org/TR/html4/strict.dtd">
<html lang="{{ locale }}"> <html lang="{{ locale }}">
<head> <head>
<script src="{{ path('minifier', { 'f' : 'assets/jquery/jquery.js,include/jslibs/jquery-validation/jquery.validate.js,include/jslibs/jquery-validate.password/jquery.validate.password.js,include/jslibs/jquery.cookie.js' }) }}" type="text/javascript"></script> <script src="{{ path('minifier', { 'f' : 'assets/jquery/jquery.js,include/jslibs/jquery-validation/jquery.validate.js,include/jslibs/jquery-validate.password/jquery.validate.password.js,assets/jquery.cookie/jquery.cookie.js' }) }}" type="text/javascript"></script>
<script type="text/javascript" src="{{ path('minifier', { 'f' : 'assets/jquery.ui/jquery-ui.js' }) }}"></script> <script type="text/javascript" src="{{ path('minifier', { 'f' : 'assets/jquery.ui/jquery-ui.js' }) }}"></script>
<script type="text/javascript" src="{{ path('minifier', { 'f' : 'include/path_files_tests.jquery.js' }) }}"></script> <script type="text/javascript" src="{{ path('minifier', { 'f' : 'include/path_files_tests.jquery.js' }) }}"></script>
<link rel="stylesheet" type="text/css" href="{{ path('minifier', { 'f' : 'include/jslibs/jquery-ui-1.10.3/css/dark-hive/jquery-ui-1.10.3.custom.css' }) }}" /> <link rel="stylesheet" type="text/css" href="{{ path('minifier', { 'f' : 'include/jslibs/jquery-ui-1.10.3/css/dark-hive/jquery-ui-1.10.3.custom.css' }) }}" />

View File

@@ -5,6 +5,7 @@ namespace Alchemy\Tests\Phrasea\Authentication;
use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application;
use Alchemy\Phrasea\Authentication\Authenticator; use Alchemy\Phrasea\Authentication\Authenticator;
use Alchemy\Phrasea\Exception\RuntimeException; use Alchemy\Phrasea\Exception\RuntimeException;
use Alchemy\Phrasea\Model\Entities\Session;
class AuthenticatorTest extends \PhraseanetPHPUnitAbstract class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
{ {
@@ -33,11 +34,17 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$app['browser'] = $browser = $this->getBrowserMock(); $app['browser'] = $browser = $this->getBrowserMock();
$app['session'] = $session = $this->getSessionMock(); $app['session'] = $session = $this->getSessionMock();
$app['EM'] = $em = $this->getEntityManagerMock();
$sessionEntity = new Session();
$sessionEntity->setUsrId($user->get_id());
$sessionEntity->setUserAgent('');
$app['EM']->persist($sessionEntity);
$app['EM']->flush();
$session->set('usr_id', $user->get_id()); $session->set('usr_id', $user->get_id());
$session->set('session_id', $sessionEntity->getId());
$authenticator = new Authenticator($app, $browser, $session, $em); $authenticator = new Authenticator($app, $browser, $session, $app['EM']);
$this->assertEquals($user, $authenticator->getUser()); $this->assertEquals($user, $authenticator->getUser());
} }
@@ -69,8 +76,7 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
public function testOpenAccount() public function testOpenAccount()
{ {
$app = new Application(); $app = new Application();
$capturedSession = null;
$sessionId = 2442;
$app['browser'] = $browser = $this->getBrowserMock(); $app['browser'] = $browser = $this->getBrowserMock();
$app['session'] = $session = $this->getSessionMock(); $app['session'] = $session = $this->getSessionMock();
@@ -97,11 +103,8 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$em->expects($this->at(0)) $em->expects($this->at(0))
->method('persist') ->method('persist')
->with($this->isInstanceOf('Alchemy\Phrasea\Model\Entities\Session')) ->with($this->isInstanceOf('Alchemy\Phrasea\Model\Entities\Session'))
->will($this->returnCallback(function ($session) use ($sessionId) { ->will($this->returnCallback(function ($session) use (&$capturedSession) {
$ref = new \ReflectionObject($session); $capturedSession = $session;
$prop = $ref->getProperty('id');
$prop->setAccessible(true);
$prop->setValue($session, $sessionId);
})); }));
$em->expects($this->at(1)) $em->expects($this->at(1))
->method('flush'); ->method('flush');
@@ -110,7 +113,7 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$phsession = $authenticator->openAccount($user); $phsession = $authenticator->openAccount($user);
$this->assertInstanceOf('Alchemy\Phrasea\Model\Entities\Session', $phsession); $this->assertInstanceOf('Alchemy\Phrasea\Model\Entities\Session', $phsession);
$this->assertEquals($sessionId, $session->get('session_id')); $this->assertEquals($capturedSession, $phsession);
} }
/** /**
@@ -129,7 +132,7 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$usrId = $user->get_id(); $usrId = $user->get_id();
$sessionId = 4224242; $sessionId = 4224242;
$session = new \Alchemy\Phrasea\Model\Entities\Session(); $session = new Session();
$session->setUsrId($usrId); $session->setUsrId($usrId);
$ref = new \ReflectionObject($session); $ref = new \ReflectionObject($session);
@@ -171,7 +174,7 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$usrId = $user->get_id(); $usrId = $user->get_id();
$sessionId = 4224242; $sessionId = 4224242;
$session = new \Alchemy\Phrasea\Model\Entities\Session(); $session = new Session();
$session->setUsrId($usrId); $session->setUsrId($usrId);
$ref = new \ReflectionObject($session); $ref = new \ReflectionObject($session);
@@ -237,11 +240,17 @@ class AuthenticatorTest extends \PhraseanetPHPUnitAbstract
$app['browser'] = $browser = $this->getBrowserMock(); $app['browser'] = $browser = $this->getBrowserMock();
$app['session'] = $session = $this->getSessionMock(); $app['session'] = $session = $this->getSessionMock();
$app['EM'] = $em = $this->getEntityManagerMock();
$sessionEntity = new Session();
$sessionEntity->setUsrId($user->get_id());
$sessionEntity->setUserAgent('');
$app['EM']->persist($sessionEntity);
$app['EM']->flush();
$session->set('usr_id', $user->get_id()); $session->set('usr_id', $user->get_id());
$session->set('session_id', $sessionEntity->getId());
$authenticator = new Authenticator($app, $browser, $session, $em); $authenticator = new Authenticator($app, $browser, $session, $app['EM']);
$this->assertTrue($authenticator->isAuthenticated()); $this->assertTrue($authenticator->isAuthenticated());
} }

View File

@@ -28,6 +28,8 @@ class FactoryTest extends \PHPUnit_Framework_TestCase
array('arraycache', null, 'Alchemy\Phrasea\Cache\ArrayCache'), array('arraycache', null, 'Alchemy\Phrasea\Cache\ArrayCache'),
array('memcache', 'memcache', 'Alchemy\Phrasea\Cache\MemcacheCache'), array('memcache', 'memcache', 'Alchemy\Phrasea\Cache\MemcacheCache'),
array('memcachecache', 'memcache', 'Alchemy\Phrasea\Cache\MemcacheCache'), array('memcachecache', 'memcache', 'Alchemy\Phrasea\Cache\MemcacheCache'),
array('memcached', 'memcached', 'Alchemy\Phrasea\Cache\MemcachedCache'),
array('memcachecached', 'memcached', 'Alchemy\Phrasea\Cache\MemcachedCache'),
array('redis', 'redis', 'Alchemy\Phrasea\Cache\RedisCache'), array('redis', 'redis', 'Alchemy\Phrasea\Cache\RedisCache'),
array('rediscache', 'redis', 'Alchemy\Phrasea\Cache\RedisCache'), array('rediscache', 'redis', 'Alchemy\Phrasea\Cache\RedisCache'),
array('wincache', 'wincache', 'Alchemy\Phrasea\Cache\WincacheCache'), array('wincache', 'wincache', 'Alchemy\Phrasea\Cache\WincacheCache'),

View File

@@ -198,10 +198,6 @@ class record_adapterTest extends PhraseanetPHPUnitAuthenticatedAbstract
public function testGet_rollover_thumbnail() public function testGet_rollover_thumbnail()
{ {
if (!extension_loaded('\Gmagick')) {
$this->markTestSkipped('\Gmagick required to build animated gifs');
}
$this->assertNull(self::$DI['record_1']->get_rollover_thumbnail()); $this->assertNull(self::$DI['record_1']->get_rollover_thumbnail());
} }

View File

@@ -9,12 +9,14 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Symfony\Component\HttpKernel\Debug\ErrorHandler; use Alchemy\Phrasea\Application;
use Symfony\Component\Debug\ErrorHandler;
require_once __DIR__ . '/../lib/autoload.php'; require_once __DIR__ . '/../lib/autoload.php';
ErrorHandler::register(); ErrorHandler::register();
$environment = Application::ENV_PROD;
$app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Api.php'; $app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Api.php';
$app->run(); $app->run();

View File

@@ -9,13 +9,14 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Symfony\Component\HttpKernel\Debug\ErrorHandler; use Alchemy\Phrasea\Application;
use Symfony\Component\Debug\ErrorHandler;
require_once __DIR__ . "/../lib/autoload.php"; require_once __DIR__ . "/../lib/autoload.php";
ErrorHandler::register(); ErrorHandler::register();
$environment = 'prod'; $environment = Application::ENV_PROD;
$app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Root.php'; $app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Root.php';
$app->run(); $app->run();

View File

@@ -9,13 +9,14 @@
* file that was distributed with this source code. * file that was distributed with this source code.
*/ */
use Symfony\Component\HttpKernel\Debug\ErrorHandler; use Alchemy\Phrasea\Application;
use Symfony\Component\Debug\ErrorHandler;
require_once __DIR__ . "/../lib/autoload.php"; require_once __DIR__ . "/../lib/autoload.php";
ErrorHandler::register(); ErrorHandler::register();
$environment = 'dev'; $environment = Application::ENV_DEV;
$app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Root.php'; $app = require __DIR__ . '/../lib/Alchemy/Phrasea/Application/Root.php';
$app->run(); $app->run();

View File

@@ -89,11 +89,22 @@ define([
"blur input#tbranch": "fieldChangedAction", "blur input#tbranch": "fieldChangedAction",
"blur input#separator": "fieldChangedAction", "blur input#separator": "fieldChangedAction",
"blur input#tag": "tagFieldChangedAction", "blur input#tag": "tagFieldChangedAction",
"change select#vocabulary-type": "triggerControlledVocabulary",
"keyup input.input-label": "labelChangedAction", "keyup input.input-label": "labelChangedAction",
"change input[type=checkbox]": "fieldChangedAction", "change input[type=checkbox]": "fieldChangedAction",
"change select": "selectionChangedAction", "change select": "selectionChangedAction",
"click .lng-label a": "_toggleLabels" "click .lng-label a": "_toggleLabels"
}, },
triggerControlledVocabulary: function(e) {
if ($(e.target, this.$el).find("option:selected").val() === "") {
this.model.set("vocabulary-type", false);
this.render();
} else if ($("input#vocabulary-restricted", this.$el).length === 0) {
this.model.set("vocabulary-restricted", false);
this.model.set("vocabulary-type", $(e.target, this.$el).find("option:selected").val());
this.render();
}
},
selectionChangedAction: function(e) { selectionChangedAction: function(e) {
var field = $(e.target); var field = $(e.target);
var data = {}; var data = {};

View File

@@ -225,6 +225,15 @@ define([
assert.isTrue(view.$('input#tag').closest(".control-group").hasClass("error")); assert.isTrue(view.$('input#tag').closest(".control-group").hasClass("error"));
}); });
it("should uncheck vocabulary restricted if provided vocabulary is empty", function() {
var view = this.view.render();
view.$('input#vocabulary-restricted').attr("checked", true);
view.$('input#vocabulary-type option').first().attr("selected", true);
assert.isTrue(false === view.$('input#vocabulary-restricted').is(":checked"));
});
}); });
describe("FieldError Views", function() { describe("FieldError Views", function() {

View File

@@ -1035,7 +1035,7 @@ function cleanTags(string)
}, { }, {
'f':">", 'f':">",
't':"&gt;" 't':"&gt;"
}, ]; } ];
for(c in chars2replace) for(c in chars2replace)
string = string.replace(RegExp(chars2replace[c].f,"g") ,chars2replace[c].t); string = string.replace(RegExp(chars2replace[c].f,"g") ,chars2replace[c].t);
return string; return string;
@@ -1929,6 +1929,10 @@ function startThisEditing(sbas_id,what,regbasprid,ssel)
}); });
} }
hsplit1();
vsplit2()
vsplit1();
$('#EDIT_TOP', p4.edit.editBox).resizable({ $('#EDIT_TOP', p4.edit.editBox).resizable({
handles : 's', handles : 's',
minHeight:100, minHeight:100,
@@ -1958,7 +1962,7 @@ function startThisEditing(sbas_id,what,regbasprid,ssel)
}); });
$('#EDIT_MID_R') $('#EDIT_MID_R')
.css('left', $('#EDIT_MID_R').position().left) .css('left', $('#EDIT_MID_L').position().left + $('#EDIT_MID_L').width() + 15)
.resizable({ .resizable({
handles : 'w', handles : 'w',
minWidth:200, minWidth:200,
@@ -2114,10 +2118,6 @@ $('#EDIT_MID_R')
ETHSeeker = new EditThesaurusSeeker(p4.edit.sbas_id); ETHSeeker = new EditThesaurusSeeker(p4.edit.sbas_id);
hsplit1();
vsplit2();
vsplit1();
setSizeLimits(); setSizeLimits();
var p = { var p = {