diff --git a/lib/Alchemy/Phrasea/Application.php b/lib/Alchemy/Phrasea/Application.php index 37b57c5b53..d556939bed 100644 --- a/lib/Alchemy/Phrasea/Application.php +++ b/lib/Alchemy/Phrasea/Application.php @@ -73,7 +73,8 @@ use Alchemy\Phrasea\Controller\Utils\ConnectionTest; use Alchemy\Phrasea\Controller\Utils\PathFileTest; use Alchemy\Phrasea\Controller\User\Notifications; use Alchemy\Phrasea\Controller\User\Preferences; -use Alchemy\Phrasea\Core\Event\Subscriber\Logout; +use Alchemy\Phrasea\Core\PhraseaExceptionHandler; +use Alchemy\Phrasea\Core\Event\Subscriber\LogoutSubscriber; use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaLocaleSubscriber; use Alchemy\Phrasea\Core\Provider\AuthenticationManagerServiceProvider; use Alchemy\Phrasea\Core\Provider\BrowserServiceProvider; @@ -121,7 +122,6 @@ use Silex\Provider\SwiftmailerServiceProvider; use Silex\Provider\UrlGeneratorServiceProvider; use Silex\Provider\ValidatorServiceProvider; use Silex\Provider\ServiceControllerServiceProvider; -use Silex\Provider\WebProfilerServiceProvider; use Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser; use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser; use Unoconv\UnoconvServiceProvider; @@ -155,34 +155,25 @@ class Application extends SilexApplication private $environment; private $sessionCookieEnabled = true; + const ENV_DEV = 'dev'; + const ENV_PROD = 'prod'; + const ENV_TEST = 'test'; + public function getEnvironment() { return $this->environment; } - public function __construct($environment = 'prod') + public function __construct($environment = self::ENV_PROD) { parent::__construct(); + error_reporting(-1); + $this['root.path'] = realpath(__DIR__ . '/../../..'); $this->environment = $environment; - if ((int) ini_get('memory_limit') < 2048) { - ini_set('memory_limit', '2048M'); - } - - error_reporting(E_ALL | E_STRICT); - - ini_set('display_errors', 'on'); - ini_set('output_buffering', '4096'); ini_set('default_charset', 'UTF-8'); - ini_set('session.use_cookies', '1'); - ini_set('session.use_only_cookies', '1'); - ini_set('session.auto_start', '0'); - ini_set('session.hash_function', '1'); - ini_set('session.hash_bits_per_character', '6'); - ini_set('session.cache_limiter', ''); - ini_set('allow_url_fopen', 'on'); mb_internal_encoding("UTF-8"); !defined('JETON_MAKE_SUBDEF') ? define('JETON_MAKE_SUBDEF', 0x01) : ''; @@ -193,17 +184,12 @@ class Application extends SilexApplication $this['charset'] = 'UTF-8'; $this['debug'] = $this->share(function(Application $app) { - return $app->getEnvironment() !== 'prod'; + return Application::ENV_PROD !== $app->getEnvironment(); }); if ($this['debug'] === true) { - ini_set('display_errors', 'on'); - if ($this->getEnvironment() === 'dev') { - ini_set('log_errors', 'on'); - ini_set('error_log', __DIR__ . '/../../../logs/php_error.log'); - } - } else { - ini_set('display_errors', 'off'); + ini_set('log_errors', 'on'); + ini_set('error_log', $this['root.path'] . '/logs/php_error.log'); } $this->register(new AuthenticationManagerServiceProvider()); @@ -263,7 +249,7 @@ class Application extends SilexApplication $this->register(new SearchEngineServiceProvider()); $this->register(new SessionServiceProvider(), array( - 'session.test' => $this->getEnvironment() == 'test' + 'session.test' => $this->getEnvironment() === static::ENV_TEST )); $this->register(new ServiceControllerServiceProvider()); $this->register(new SwiftmailerServiceProvider()); @@ -272,7 +258,7 @@ class Application extends SilexApplication $this->register(new TokensServiceProvider()); $this->register(new TwigServiceProvider(), array( 'twig.options' => array( - 'cache' => realpath(__DIR__ . '/../../../tmp/cache_twig/'), + 'cache' => $this['root.path'] . '/tmp/cache_twig/', ), 'twig.form.templates' => array('login/common/form_div_layout.html.twig') )); @@ -285,19 +271,14 @@ class Application extends SilexApplication $this->register(new UnicodeServiceProvider()); $this->register(new ValidatorServiceProvider()); - if ('dev' === $this->environment) { - $this->register($p = new WebProfilerServiceProvider(), array( - 'profiler.cache_dir' => __DIR__ . '/../../../tmp/cache/profiler', - )); - $this->mount('/_profiler', $p); - } - $this->register(new XPDFServiceProvider()); + $this['phraseanet.exception_handler'] = $this->share(function ($app) { + return PhraseaExceptionHandler::register($app['debug']); + }); + $this['swiftmailer.transport'] = $this->share(function ($app) { - if ($app['phraseanet.registry']->get('GV_smtp')) { - $transport = new \Swift_Transport_EsmtpTransport( $app['swiftmailer.transport.buffer'], array($app['swiftmailer.transport.authhandler']), @@ -340,9 +321,6 @@ class Application extends SilexApplication return $transport; }); -// $this->register(new \Silex\Provider\HttpCacheServiceProvider()); -// $this->register(new \Silex\Provider\SecurityServiceProvider()); - $this['imagine.factory'] = $this->share(function(Application $app) { if ($app['phraseanet.registry']->get('GV_imagine_driver') != '') { return $app['phraseanet.registry']->get('GV_imagine_driver'); @@ -384,11 +362,17 @@ class Application extends SilexApplication }) ); - $this['dispatcher']->addListener(KernelEvents::REQUEST, array($this, 'initSession'), 254); - $this['dispatcher']->addListener(KernelEvents::RESPONSE, array($this, 'addUTF8Charset'), -128); - $this['dispatcher']->addListener(KernelEvents::RESPONSE, array($this, 'disableCookiesIfRequired'), -256); - $this['dispatcher']->addSubscriber(new Logout()); - $this['dispatcher']->addSubscriber(new PhraseaLocaleSubscriber($this)); + $this['dispatcher'] = $this->share( + $this->extend('dispatcher', function($dispatcher, Application $app){ + $dispatcher->addListener(KernelEvents::REQUEST, array($app, 'initSession'), 254); + $dispatcher->addListener(KernelEvents::RESPONSE, array($app, 'addUTF8Charset'), -128); + $dispatcher->addListener(KernelEvents::RESPONSE, array($app, 'disableCookiesIfRequired'), -256); + $dispatcher->addSubscriber(new LogoutSubscriber()); + $dispatcher->addSubscriber(new PhraseaLocaleSubscriber($app)); + + return $dispatcher; + }) + ); $this->register(new LocaleServiceProvider()); diff --git a/lib/Alchemy/Phrasea/Application/Api.php b/lib/Alchemy/Phrasea/Application/Api.php index ed77685b45..7a5f9e8dfa 100644 --- a/lib/Alchemy/Phrasea/Application/Api.php +++ b/lib/Alchemy/Phrasea/Application/Api.php @@ -17,17 +17,19 @@ use Alchemy\Phrasea\Controller\Api\Oauth2; use Alchemy\Phrasea\Controller\Api\V1; use Alchemy\Phrasea\Core\Event\ApiLoadEndEvent; use Alchemy\Phrasea\Core\Event\ApiLoadStartEvent; +use Alchemy\Phrasea\Core\Event\Subscriber\ApiOauth2ErrorsSubscriber; +use Alchemy\Phrasea\Core\Event\Subscriber\ApiExceptionHandlerSubscriber; use Silex\Application as SilexApplication; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -use Symfony\Component\HttpKernel\Exception\HttpException; -use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\Response; -return call_user_func(function($environment = 'prod') { +return call_user_func(function($environment = PhraseaApplication::ENV_PROD) { $app = new PhraseaApplication($environment); + + $app['exception_handler'] = $app->share(function ($app) { + return new ApiExceptionHandlerSubscriber($app); + }); + $app->disableCookies(); $app->register(new \API_V1_Timer()); @@ -61,72 +63,7 @@ return call_user_func(function($environment = 'prod') { $app->mount('/api/oauthv2', new Oauth2()); $app->mount('/api/v1', new V1()); - /** - * Route Errors - */ - $app->error(function (\Exception $e) use ($app) { - - $request = $app['request']; - - if (0 === strpos($request->getPathInfo(), '/api/oauthv2')) { - if ($e instanceof NotFoundHttpException || $e instanceof \Exception_NotFound) { - return new Response('The requested page could not be found.', 404, array('X-Status-Code' => 404)); - } - - $code = 500; - $msg = 'We are sorry, but something went wrong'; - $headers = array(); - - if ($e instanceof HttpExceptionInterface) { - $headers = $e->getHeaders(); - $msg = $e->getMessage(); - $code = $e->getStatusCode(); - - if (isset($headers['content-type']) && $headers['content-type'] == 'application/json') { - $msg = json_encode(array('msg' => $msg, 'code' => $code)); - } - } - - return new Response($msg, $code, $headers); - } - - $headers = array(); - - if ($e instanceof \API_V1_exception_methodnotallowed) { - $code = \API_V1_result::ERROR_METHODNOTALLOWED; - } elseif ($e instanceof MethodNotAllowedHttpException) { - $code = \API_V1_result::ERROR_METHODNOTALLOWED; - } elseif ($e instanceof \API_V1_exception_badrequest) { - $code = \API_V1_result::ERROR_BAD_REQUEST; - } elseif ($e instanceof \API_V1_exception_forbidden) { - $code = \API_V1_result::ERROR_FORBIDDEN; - } elseif ($e instanceof \API_V1_exception_unauthorized) { - $code = \API_V1_result::ERROR_UNAUTHORIZED; - } elseif ($e instanceof \API_V1_exception_internalservererror) { - $code = \API_V1_result::ERROR_INTERNALSERVERERROR; - } elseif ($e instanceof \Exception_NotFound) { - $code = \API_V1_result::ERROR_NOTFOUND; - } elseif ($e instanceof NotFoundHttpException) { - $code = \API_V1_result::ERROR_NOTFOUND; - } else { - $code = \API_V1_result::ERROR_INTERNALSERVERERROR; - } - - if ($e instanceof HttpException) { - $headers = $e->getHeaders(); - } - - $result = $app['api']->get_error_message($app['request'], $code, $e->getMessage()); - $response = $result->get_response(); - $response->headers->set('X-Status-Code', $result->get_http_code()); - - foreach ($headers as $key => $value) { - $response->headers->set($key, $value); - } - - return $response; - }); - + $app['dispatcher']->addSubscriber(new ApiOauth2ErrorsSubscriber($app['phraseanet.exception_handler'])); $app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_END, new ApiLoadEndEvent()); return $app; diff --git a/lib/Alchemy/Phrasea/Application/Root.php b/lib/Alchemy/Phrasea/Application/Root.php index b8c482eff6..95103cfb27 100644 --- a/lib/Alchemy/Phrasea/Application/Root.php +++ b/lib/Alchemy/Phrasea/Application/Root.php @@ -12,15 +12,22 @@ namespace Alchemy\Phrasea\Application; use Alchemy\Phrasea\Application as PhraseaApplication; +use Alchemy\Phrasea\Core\Event\Subscriber\PhraseaExceptionHandlerSubscriber; +use Alchemy\Phrasea\Core\Event\Subscriber\BridgeExceptionSubscriber; +use Alchemy\Phrasea\Core\Event\Subscriber\FirewallSubscriber; +use Alchemy\Phrasea\Core\Event\Subscriber\JsonRequestSubscriber; +use Alchemy\Phrasea\Core\Event\Subscriber\DebuggerSubscriber; +use Silex\Provider\WebProfilerServiceProvider; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; -return call_user_func(function($environment = null) { +return call_user_func(function($environment = PhraseaApplication::ENV_PROD) { $app = new PhraseaApplication($environment); + $app['exception_handler'] = $app->share(function ($app) { + return new PhraseaExceptionHandlerSubscriber($app['phraseanet.exception_handler']); + }); + $app->before(function (Request $request) use ($app) { if (0 === strpos($request->getPathInfo(), '/setup')) { if (!$app['phraseanet.configuration-tester']->isBlank()) { @@ -41,98 +48,23 @@ return call_user_func(function($environment = null) { $app->bindRoutes(); - $app->error(function(\Exception $e) use ($app) { - $request = $app['request']; + if (PhraseaApplication::ENV_DEV === $app->getEnvironment()) { + $app->register(new WebProfilerServiceProvider(), array( + 'profiler.cache_dir' => $app['root.path'] . '/tmp/cache/profiler', + 'profiler.mount_prefix' => '/_profiler', + )); + } - if ($e instanceof \Bridge_Exception) { - $params = array( - 'message' => $e->getMessage() - , 'file' => $e->getFile() - , 'line' => $e->getLine() - , 'r_method' => $request->getMethod() - , 'r_action' => $request->getRequestUri() - , 'r_parameters' => ($request->getMethod() == 'GET' ? array() : $request->request->all()) - ); + $app['dispatcher'] = $app->share( + $app->extend('dispatcher', function($dispatcher, PhraseaApplication $app){ + $dispatcher->addSubscriber(new BridgeExceptionSubscriber($app)); + $dispatcher->addSubscriber(new FirewallSubscriber()); + $dispatcher->addSubscriber(new JsonRequestSubscriber()); + $dispatcher->addSubscriber(new DebuggerSubscriber($app)); - if ($e instanceof \Bridge_Exception_ApiConnectorNotConfigured) { - $params = array_merge($params, array('account' => $app['current_account'])); - - $response = new Response($app['twig']->render('/prod/actions/Bridge/notconfigured.html.twig', $params), 200, array('X-Status-Code' => 200)); - } elseif ($e instanceof \Bridge_Exception_ApiConnectorNotConnected) { - $params = array_merge($params, array('account' => $app['current_account'])); - - $response = new Response($app['twig']->render('/prod/actions/Bridge/disconnected.html.twig', $params), 200, array('X-Status-Code' => 200)); - } elseif ($e instanceof \Bridge_Exception_ApiConnectorAccessTokenFailed) { - $params = array_merge($params, array('account' => $app['current_account'])); - - $response = new Response($app['twig']->render('/prod/actions/Bridge/disconnected.html.twig', $params), 200, array('X-Status-Code' => 200)); - } elseif ($e instanceof \Bridge_Exception_ApiDisabled) { - $params = array_merge($params, array('api' => $e->get_api())); - - $response = new Response($app['twig']->render('/prod/actions/Bridge/deactivated.html.twig', $params), 200, array('X-Status-Code' => 200)); - } else { - $response = new Response($app['twig']->render('/prod/actions/Bridge/error.html.twig', $params), 200, array('X-Status-Code' => 200)); - } - - $response->headers->set('Phrasea-StatusCode', 200); - - return $response; - } - - if ((0 !== strpos($request->getPathInfo(), '/admin/') - || 0 === strpos($request->getPathInfo(), '/admin/collection/') - || 0 === strpos($request->getPathInfo(), '/admin/databox/')) - && $request->getRequestFormat() == 'json') { - $datas = array( - 'success' => false - , 'message' => $e->getMessage() - ); - - return $app->json($datas, 200, array('X-Status-Code' => 200)); - } - - if ($e instanceof HttpExceptionInterface) { - $headers = $e->getHeaders(); - - if (isset($headers['X-Phraseanet-Redirect'])) { - return new RedirectResponse($headers['X-Phraseanet-Redirect'], 302, array('X-Status-Code' => 302)); - } - - $message = isset(Response::$statusTexts[$e->getStatusCode()]) ? Response::$statusTexts[$e->getStatusCode()] : ''; - - if (400 === $e->getStatusCode()) { - $message .= ' : ' . $e->getMessage(); - } - - return new Response($message, $e->getStatusCode(), $e->getHeaders()); - } - - if ($e instanceof \Exception_BadRequest) { - return new Response('Bad Request', 400, array('X-Status-Code' => 400)); - } - if ($e instanceof \Exception_Forbidden) { - return new Response('Forbidden', 403, array('X-Status-Code' => 403)); - } - - if ($e instanceof \Exception_Session_NotAuthenticated) { - $code = 403; - $message = 'Forbidden'; - } elseif ($e instanceof \Exception_NotAllowed) { - $code = 403; - $message = 'Forbidden'; - } elseif ($e instanceof \Exception_NotFound) { - $code = 404; - $message = 'Not Found'; - } elseif ($e instanceof \Exception_UnauthorizedAction) { - $code = 403; - $message = 'Forbidden'; - } else { - $code = 500; - $message = 'Server Error' . ($app['debug'] ? ' : ' . $e->getMessage() : ''); - } - - return new Response($message, $code, array('X-Status-Code' => $code)); - }); + return $dispatcher; + }) + ); return $app; }, isset($environment) ? $environment : null); diff --git a/lib/Alchemy/Phrasea/Authentication/Authenticator.php b/lib/Alchemy/Phrasea/Authentication/Authenticator.php index e0031266f9..0874804dde 100644 --- a/lib/Alchemy/Phrasea/Authentication/Authenticator.php +++ b/lib/Alchemy/Phrasea/Authentication/Authenticator.php @@ -17,6 +17,7 @@ use Browser; use Doctrine\ORM\EntityManager; use Entities\Session; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class Authenticator { @@ -93,7 +94,7 @@ class Authenticator try { $user = \User_Adapter::getInstance($session->getUsrId(), $this->app); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { throw new RuntimeException('Unable to refresh the session', $e->getCode(), $e); } diff --git a/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php b/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php index 5eb2474eb6..f9a65836ec 100644 --- a/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php +++ b/lib/Alchemy/Phrasea/Authentication/Token/TokenValidator.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Authentication\Token; use Alchemy\Phrasea\Application; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class TokenValidator { @@ -34,7 +35,7 @@ class TokenValidator $datas = $this->app['tokens']->helloToken($token); return $datas['usr_id']; - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { } diff --git a/lib/Alchemy/Phrasea/Border/Attribute/MetaField.php b/lib/Alchemy/Phrasea/Border/Attribute/MetaField.php index f292e4a562..ee6eba475d 100644 --- a/lib/Alchemy/Phrasea/Border/Attribute/MetaField.php +++ b/lib/Alchemy/Phrasea/Border/Attribute/MetaField.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Border\Attribute; use Alchemy\Phrasea\Application; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Phraseanet Border MetaField Attribute @@ -108,7 +109,7 @@ class MetaField implements AttributeInterface return new static($app['phraseanet.appbox'] ->get_databox($datas['sbas_id']) ->get_meta_structure()->get_element($datas['id']), $datas['value']); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { throw new \InvalidArgumentException('Field does not exist anymore'); } } diff --git a/lib/Alchemy/Phrasea/Border/Attribute/Story.php b/lib/Alchemy/Phrasea/Border/Attribute/Story.php index 2aa5b15819..4d0ce66b4e 100644 --- a/lib/Alchemy/Phrasea/Border/Attribute/Story.php +++ b/lib/Alchemy/Phrasea/Border/Attribute/Story.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Border\Attribute; use Alchemy\Phrasea\Application; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Phraseanet Border Story Attribute @@ -82,7 +83,7 @@ class Story implements AttributeInterface try { $story = new \record_adapter($app, $ids[0], $ids[1]); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { throw new \InvalidArgumentException('Unable to fetch a story from string'); } diff --git a/lib/Alchemy/Phrasea/Border/Manager.php b/lib/Alchemy/Phrasea/Border/Manager.php index 2390a8ee2f..4d205f52f7 100644 --- a/lib/Alchemy/Phrasea/Border/Manager.php +++ b/lib/Alchemy/Phrasea/Border/Manager.php @@ -219,11 +219,11 @@ class Manager */ protected function bookLazaretPathfile($filename, $suffix = '') { - $output = __DIR__ . '/../../../../tmp/lazaret/lzrt_' . substr($filename, 0, 3) . '_' . $suffix . '.' . pathinfo($filename, PATHINFO_EXTENSION); + $output = $this->app['root.path'] . '/tmp/lazaret/lzrt_' . substr($filename, 0, 3) . '_' . $suffix . '.' . pathinfo($filename, PATHINFO_EXTENSION); $infos = pathinfo($output); $n = 0; - $this->app['filesystem']->mkdir(__DIR__ . '/../../../../tmp/lazaret'); + $this->app['filesystem']->mkdir($this->app['root.path'] . '/tmp/lazaret'); while (true) { $output = sprintf('%s/%s-%d%s', $infos['dirname'], $infos['filename'], ++ $n, (isset($infos['extension']) ? '.' . $infos['extension'] : '')); diff --git a/lib/Alchemy/Phrasea/Command/Compile/Configuration.php b/lib/Alchemy/Phrasea/Command/Compile/Configuration.php index 7b876944e1..a4c7a7730a 100644 --- a/lib/Alchemy/Phrasea/Command/Compile/Configuration.php +++ b/lib/Alchemy/Phrasea/Command/Compile/Configuration.php @@ -27,7 +27,7 @@ class Configuration extends Command { $this->container['phraseanet.configuration']->compileAndWrite(); $output->writeln("Confguration compiled."); - + return 0; } } diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Publications.php b/lib/Alchemy/Phrasea/Controller/Admin/Publications.php index 20e77170a4..769143246f 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Publications.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Publications.php @@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application as PhraseaApplication; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; /** * @@ -111,23 +112,23 @@ class Publications implements ControllerProviderInterface try { if (!$request->files->get('files')) { - throw new \Exception_BadRequest('Missing file parameter'); + throw new BadRequestHttpException('Missing file parameter'); } if (count($request->files->get('files')) > 1) { - throw new \Exception_BadRequest('Upload is limited to 1 file per request'); + throw new BadRequestHttpException('Upload is limited to 1 file per request'); } $file = current($request->files->get('files')); if (!$file->isValid()) { - throw new \Exception_BadRequest('Uploaded file is invalid'); + throw new BadRequestHttpException('Uploaded file is invalid'); } $media = $app['mediavorus']->guess($file->getPathname()); if ($media->getType() !== \MediaVorus\Media\MediaInterface::TYPE_IMAGE) { - throw new \Exception_BadRequest('Bad filetype'); + throw new BadRequestHttpException('Bad filetype'); } $spec = new \MediaAlchemyst\Specification\Image(); diff --git a/lib/Alchemy/Phrasea/Controller/Admin/Root.php b/lib/Alchemy/Phrasea/Controller/Admin/Root.php index 5a73d7531f..039d147190 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/Root.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/Root.php @@ -15,6 +15,7 @@ use Alchemy\Phrasea\Exception\SessionNotFound; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * @@ -340,7 +341,7 @@ class Root implements ControllerProviderInterface if (null !== $file = $request->files->get('image_off')) { try { \databox_status::updateIcon($app, $databox_id, $bit, 'off', $file); - } catch (\Exception_Forbidden $e) { + } catch (AccessDeniedHttpException $e) { return $app->redirectPath('database_display_statusbit_form', array( 'databox_id' => $databox_id, 'bit' => $bit, @@ -386,7 +387,7 @@ class Root implements ControllerProviderInterface if (null !== $file = $request->files->get('image_on')) { try { \databox_status::updateIcon($app, $databox_id, $bit, 'on', $file); - } catch (\Exception_Forbidden $e) { + } catch (AccessDeniedHttpException $e) { return $app->redirectPath('database_display_statusbit_form', array( 'databox_id' => $databox_id, 'bit' => $bit, diff --git a/lib/Alchemy/Phrasea/Controller/Admin/TaskManager.php b/lib/Alchemy/Phrasea/Controller/Admin/TaskManager.php index e7aaf0553f..59400b8202 100644 --- a/lib/Alchemy/Phrasea/Controller/Admin/TaskManager.php +++ b/lib/Alchemy/Phrasea/Controller/Admin/TaskManager.php @@ -165,6 +165,7 @@ class TaskManager implements ControllerProviderInterface /** * todo : add a message back */ + return $app->redirectPath('admin_tasks_list'); } })->bind('admin_tasks_task_delete'); diff --git a/lib/Alchemy/Phrasea/Controller/Datafiles.php b/lib/Alchemy/Phrasea/Controller/Datafiles.php index 3e2690224c..628b763580 100644 --- a/lib/Alchemy/Phrasea/Controller/Datafiles.php +++ b/lib/Alchemy/Phrasea/Controller/Datafiles.php @@ -15,6 +15,7 @@ use Alchemy\Phrasea\Application as PhraseaApplication; use Silex\Application; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * @@ -63,7 +64,7 @@ class Datafiles extends AbstractDelivery } if (!$app['authentication']->getUser()->ACL()->has_access_to_subdef($record, $subdef)) { - throw new \Exception_UnauthorizedAction(sprintf('User has not access to subdef %s', $subdef)); + throw new AccessDeniedHttpException(sprintf('User has not access to subdef %s', $subdef)); } $stamp = false; diff --git a/lib/Alchemy/Phrasea/Controller/Lightbox.php b/lib/Alchemy/Phrasea/Controller/Lightbox.php index d7cd0d97bb..2fd0708efd 100644 --- a/lib/Alchemy/Phrasea/Controller/Lightbox.php +++ b/lib/Alchemy/Phrasea/Controller/Lightbox.php @@ -17,6 +17,7 @@ use Silex\ControllerProviderInterface; use Silex\Application as SilexApplication; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class Lightbox implements ControllerProviderInterface { @@ -43,7 +44,7 @@ class Lightbox implements ControllerProviderInterface try { $datas = $app['tokens']->helloToken($request->query->get('LOG')); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { return; } switch ($datas['type']) { diff --git a/lib/Alchemy/Phrasea/Controller/Minifier.php b/lib/Alchemy/Phrasea/Controller/Minifier.php index e669337d9c..7f2ea50735 100644 --- a/lib/Alchemy/Phrasea/Controller/Minifier.php +++ b/lib/Alchemy/Phrasea/Controller/Minifier.php @@ -26,7 +26,7 @@ class Minifier implements ControllerProviderInterface $controllers->get('/', function (Application $app, Request $request) { // cache directory path - $min_cachePath = __DIR__ . '/../../../../tmp/cache_minify'; + $min_cachePath = $app['root.path'] . '/tmp/cache_minify'; /** * Cache file locking. Set to false if filesystem is NFS. On at least one diff --git a/lib/Alchemy/Phrasea/Controller/Permalink.php b/lib/Alchemy/Phrasea/Controller/Permalink.php index 8779a2d2ed..28c31a5fab 100644 --- a/lib/Alchemy/Phrasea/Controller/Permalink.php +++ b/lib/Alchemy/Phrasea/Controller/Permalink.php @@ -37,7 +37,7 @@ class Permalink extends AbstractDelivery $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); if (!$record instanceof \record_adapter) { - throw new \Exception_NotFound('bad luck'); + throw new NotFoundHttpException('bad luck'); } $params = array( @@ -56,7 +56,7 @@ class Permalink extends AbstractDelivery $record = \media_Permalink_Adapter::challenge_token($app, $databox, $token, $record_id, $subdef); if (!($record instanceof \record_adapter)) { - throw new \Exception_NotFound('bad luck'); + throw new NotFoundHttpException('bad luck'); } $watermark = $stamp = false; diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Basket.php b/lib/Alchemy/Phrasea/Controller/Prod/Basket.php index 389c37fd1e..f70307784c 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Basket.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Basket.php @@ -18,6 +18,8 @@ use Entities\ValidationData; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -272,9 +274,9 @@ class Basket implements ControllerProviderInterface $success = true; $msg = _('Basket has been updated'); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $msg = _('The requested basket does not exist'); - } catch (\Exception_Forbidden $e) { + } catch (AccessDeniedHttpException $e) { $msg = _('You do not have access to this basket'); } catch (\Exception $e) { $msg = _('An error occurred'); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Bridge.php b/lib/Alchemy/Phrasea/Controller/Prod/Bridge.php index 86aa32c332..0b3b6d671c 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Bridge.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Bridge.php @@ -17,433 +17,456 @@ use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; -/** - * - * @license http://opensource.org/licenses/gpl-3.0 GPLv3 - * @link www.phraseanet.com - */ class Bridge implements ControllerProviderInterface { - public function connect(Application $app) { $controllers = $app['controllers_factory']; $controllers->before(function(Request $request) use ($app) { - $app['firewall'] - ->requireNotGuest() - ->requireRight('bas_chupub'); + $app['firewall']->requireRight('bas_chupub'); }); - $app['require_connection'] = $app->protect(function(\Bridge_Account $account) use ($app) { - $app['current_account'] = function() use ($account) { - return $account; - }; + $app['bridge.controller'] = $this; - if (!$account->get_api()->get_connector()->is_configured()) - throw new \Bridge_Exception_ApiConnectorNotConfigured("Bridge API Connector is not configured"); - if (!$account->get_api()->get_connector()->is_connected()) - throw new \Bridge_Exception_ApiConnectorNotConnected("Bridge API Connector is not connected"); + $controllers + ->post('/manager/', 'bridge.controller:doPostManager'); - return; - }); + $controllers + ->get('/login/{api_name}/', 'bridge.controller:doGetLogin') + ->bind('prod_bridge_login'); - $controllers->post('/manager/', function(Application $app) { - $route = new RecordHelper\Bridge($app, $app['request']); + $controllers + ->get('/callback/{api_name}/', 'bridge.controller:doGetCallback') + ->bind('prod_bridge_callback'); - $params = array( - 'user_accounts' => \Bridge_Account::get_accounts_by_user($app, $app['authentication']->getUser()) - , 'available_apis' => \Bridge_Api::get_availables($app) - , 'route' => $route - , 'current_account_id' => '' - ); - - return $app['twig']->render('prod/actions/Bridge/index.html.twig', $params); - }); - - $controllers->get('/login/{api_name}/', function(Application $app, $api_name) { - $connector = \Bridge_Api::get_connector_by_name($app, $api_name); - - return $app->redirect($connector->get_auth_url()); - })->bind('prod_bridge_login'); - - $controllers->get('/callback/{api_name}/', function(Application $app, $api_name) { - $error_message = ''; - try { - $api = \Bridge_Api::get_by_api_name($app, $api_name); - $connector = $api->get_connector(); - - $response = $connector->connect(); - - $user_id = $connector->get_user_id(); - - try { - $account = \Bridge_Account::load_account_from_distant_id($app, $api, $app['authentication']->getUser(), $user_id); - } catch (\Bridge_Exception_AccountNotFound $e) { - $account = \Bridge_Account::create($app, $api, $app['authentication']->getUser(), $user_id, $connector->get_user_name()); - } - $settings = $account->get_settings(); - - if (isset($response['auth_token'])) - $settings->set('auth_token', $response['auth_token']); - if (isset($response['refresh_token'])) - $settings->set('refresh_token', $response['refresh_token']); - - $connector->set_auth_settings($settings); - - $connector->reconnect(); - } catch (\Exception $e) { - $error_message = $e->getMessage(); - } - - $params = array('error_message' => $error_message); - - return $app['twig']->render('prod/actions/Bridge/callback.html.twig', $params); - })->bind('prod_bridge_callback'); - - $controllers->get('/adapter/{account_id}/logout/', function(Application $app, $account_id) { - $account = \Bridge_Account::load_account($app, $account_id); - $app['require_connection']($account); - $account->get_api()->get_connector()->disconnect(); - - return $app->redirectPath('bridge_load_elements', array( - 'account_id' => $account_id, - 'type' => $account->get_api()->get_connector()->get_default_element_type(), - )); - }) + $controllers + ->get('/adapter/{account_id}/logout/', 'bridge.controller:doGetAccountLogout') ->bind('prod_bridge_account_logout') ->assert('account_id', '\d+'); - $controllers->post('/adapter/{account_id}/delete/' - , function($account_id) use ($app) { - $success = false; - $message = ''; - try { - $account = \Bridge_Account::load_account($app, $account_id); + $controllers + ->post('/adapter/{account_id}/delete/', 'bridge.controller:doPostAccountDelete') + ->assert('account_id', '\d+'); - if ($account->get_user()->get_id() !== $app['authentication']->getUser()->get_id()) { - throw new HttpException(403, 'Access forbiden'); - } - - $account->delete(); - $success = true; - } catch (\Bridge_Exception_AccountNotFound $e) { - $message = _('Account is not found.'); - } catch (\Exception $e) { - $message = _('Something went wrong, please contact an administrator'); - } - - return $app->json(array('success' => $success, 'message' => $message)); - })->assert('account_id', '\d+'); - - $controllers->get('/adapter/{account_id}/load-records/', function(Application $app, $account_id) { - $page = max((int) $app['request']->query->get('page'), 0); - $quantity = 10; - $offset_start = max(($page - 1) * $quantity, 0); - $account = \Bridge_Account::load_account($app, $account_id); - $elements = \Bridge_Element::get_elements_by_account($app, $account, $offset_start, $quantity); - - $app['require_connection']($account); - - $params = array( - 'adapter_action' => 'load-records' - , 'account' => $account - , 'elements' => $elements - , 'error_message' => $app['request']->query->get('error') - , 'notice_message' => $app['request']->query->get('notice') - ); - - return $app['twig']->render('prod/actions/Bridge/records_list.html.twig', $params); - }) + $controllers + ->get('/adapter/{account_id}/load-records/', 'bridge.controller:doGetloadRecords') ->bind('prod_bridge_account_loadrecords') ->assert('account_id', '\d+'); - $controllers->get('/adapter/{account_id}/load-elements/{type}/', function($account_id, $type) use ($app) { - $page = max((int) $app['request']->query->get('page'), 0); - $quantity = 5; - $offset_start = max(($page - 1) * $quantity, 0); - $account = \Bridge_Account::load_account($app, $account_id); - - $app['require_connection']($account); - - $elements = $account->get_api()->list_elements($type, $offset_start, $quantity); - - $params = array( - 'action_type' => $type - , 'adapter_action' => 'load-elements' - , 'account' => $account - , 'elements' => $elements - , 'error_message' => $app['request']->query->get('error') - , 'notice_message' => $app['request']->query->get('notice') - ); - - return $app['twig']->render('prod/actions/Bridge/element_list.html.twig', $params); - }) + $controllers + ->get('/adapter/{account_id}/load-elements/{type}/', 'bridge.controller:doGetLoadElements') ->bind('bridge_load_elements') ->assert('account_id', '\d+'); - $controllers->get('/adapter/{account_id}/load-containers/{type}/', function(Application $app, $account_id, $type) { - - $page = max((int) $app['request']->query->get('page'), 0); - $quantity = 5; - $offset_start = max(($page - 1) * $quantity, 0); - $account = \Bridge_Account::load_account($app, $account_id); - - $app['require_connection']($account); - $elements = $account->get_api()->list_containers($type, $offset_start, $quantity); - - $params = array( - 'action_type' => $type - , 'adapter_action' => 'load-containers' - , 'account' => $account - , 'elements' => $elements - , 'error_message' => $app['request']->query->get('error') - , 'notice_message' => $app['request']->query->get('notice') - ); - - return $app['twig']->render('prod/actions/Bridge/element_list.html.twig', $params); - }) + $controllers + ->get('/adapter/{account_id}/load-containers/{type}/', 'bridge.controller:doGetLoadContainers') ->bind('prod_bridge_account_loadcontainers') ->assert('account_id', '\d+'); - $controllers->get('/action/{account_id}/{action}/{element_type}/', function(Application $app, $account_id, $action, $element_type) { - - $account = \Bridge_Account::load_account($app, $account_id); - - $app['require_connection']($account); - $request = $app['request']; - $elements = $request->query->get('elements_list', array()); - $elements = is_array($elements) ? $elements : explode(';', $elements); - - $destination = $request->query->get('destination'); - $route_params = array(); - $class = $account->get_api()->get_connector()->get_object_class_from_type($element_type); - - switch ($action) { - case 'createcontainer': - break; - - case 'modify': - if (count($elements) != 1) { - return $app->redirectPath('bridge_load_elements', array( - 'account_id' => $account_id, - 'type' => $element_type, - 'page' => '', - 'error' => _('Vous ne pouvez pas editer plusieurs elements simultanement'), - )); - } - foreach ($elements as $element_id) { - if ($class === \Bridge_Api_Interface::OBJECT_CLASS_ELEMENT) { - $route_params = array('element' => $account->get_api()->get_element_from_id($element_id, $element_type)); - } - if ($class === \Bridge_Api_Interface::OBJECT_CLASS_CONTAINER) { - $route_params = array('element' => $account->get_api()->get_container_from_id($element_id, $element_type)); - } - } - break; - - case 'moveinto': - - $route_params = array('containers' => $account->get_api()->list_containers($destination, 0, 0)); - break; - - case 'deleteelement': - - break; - - default: - throw new \Exception(_('Vous essayez de faire une action que je ne connais pas !')); - break; - } - - $params = array( - 'account' => $account - , 'destination' => $destination - , 'element_type' => $element_type - , 'action' => $action - , 'constraint_errors' => null - , 'adapter_action' => $action - , 'elements' => $elements - , 'error_message' => $app['request']->query->get('error') - , 'notice_message' => $app['request']->query->get('notice') - ); - - $params = array_merge($params, $route_params); - - $template = 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/' . $element_type . '_' . $action . ($destination ? '_' . $destination : '') . '.html.twig'; - - return $app['twig']->render($template, $params); - }) + $controllers + ->get('/action/{account_id}/{action}/{element_type}/', 'bridge.controller:doGetAction') ->bind('bridge_account_action') ->assert('account_id', '\d+'); - $controllers->post('/action/{account_id}/{action}/{element_type}/', function(Application $app, $account_id, $action, $element_type) { - $account = \Bridge_Account::load_account($app, $account_id); - - $app['require_connection']($account); - - $request = $app['request']; - $elements = $request->request->get('elements_list', array()); - $elements = is_array($elements) ? $elements : explode(';', $elements); - - $destination = $request->request->get('destination'); - - $class = $account->get_api()->get_connector()->get_object_class_from_type($element_type); - $html = ''; - switch ($action) { - case 'modify': - if (count($elements) != 1) { - return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?elements_list=' . implode(';', $elements) . '&error=' . _('Vous ne pouvez pas editer plusieurs elements simultanement')); - } - try { - foreach ($elements as $element_id) { - $datas = $account->get_api()->get_connector()->get_update_datas($app['request']); - $errors = $account->get_api()->get_connector()->check_update_constraints($datas); - } - - if (count($errors) > 0) { - $params = array( - 'element' => $account->get_api()->get_element_from_id($element_id, $element_type) - , 'account' => $account - , 'destination' => $destination - , 'element_type' => $element_type - , 'action' => $action - , 'elements' => $elements - , 'adapter_action' => $action - , 'error_message' => _('Request contains invalid datas') - , 'constraint_errors' => $errors - , 'notice_message' => $app['request']->request->get('notice') - ); - - $template = 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/' . $element_type . '_' . $action . ($destination ? '_' . $destination : '') . '.html.twig'; - - return $app['twig']->render($template, $params); - } - - foreach ($elements as $element_id) { - $datas = $account->get_api()->get_connector()->get_update_datas($app['request']); - $account->get_api()->update_element($element_type, $element_id, $datas); - } - } catch (\Exception $e) { - return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?elements_list[]=' . $element_id . '&error=' . get_class($e) . ' : ' . $e->getMessage()); - } - - return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/?page=&update=success#anchor'); - - break; - case 'createcontainer': - try { - - $container_type = $request->request->get('f_container_type'); - - $account->get_api()->create_container($container_type, $app['request']); - } catch (\Exception $e) { - return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . ' : ' . $e->getMessage()); - } - - return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/?page=&update=success#anchor'); - - break; - case 'moveinto': - try { - $container_id = $request->request->get('container_id'); - foreach ($elements as $element_id) { - $account->get_api()->add_element_to_container($element_type, $element_id, $destination, $container_id); - } - } catch (\Exception $e) { - return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . ' : ' . $e->getMessage()); - } - - return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-containers/' . $destination . '/?page=&update=success#anchor'); - - break; - - case 'deleteelement': - try { - foreach ($elements as $element_id) { - $account->get_api()->delete_object($element_type, $element_id); - } - } catch (\Exception $e) { - return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . $e->getMessage()); - } - - return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/'); - break; - default: - throw new \Exception('Unknown action'); - break; - } - - return new Response($html); - }) + $controllers + ->post('/action/{account_id}/{action}/{element_type}/', 'bridge.controller:doPostAction') ->bind('bridge_account_do_action') ->assert('account_id', '\d+'); - $controllers->get('/upload/', function(Application $app) { - $request = $app['request']; - $account = \Bridge_Account::load_account($app, $request->query->get('account_id')); - $app['require_connection']($account); + $controllers + ->get('/upload/', 'bridge.controller:doGetUpload') + ->bind('prod_bridge_upload'); - $route = new RecordHelper\Bridge($app, $app['request']); - - $route->grep_records($account->get_api()->acceptable_records()); - - $params = array( - 'route' => $route - , 'account' => $account - , 'error_message' => $app['request']->query->get('error') - , 'notice_message' => $app['request']->query->get('notice') - , 'constraint_errors' => null - , 'adapter_action' => 'upload' - ); - - return $app['twig']->render( - 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/upload.html.twig', $params - ); - })->bind('prod_bridge_upload'); - - $controllers->post('/upload/', function(Application $app) { - $errors = array(); - $request = $app['request']; - $account = \Bridge_Account::load_account($app, $request->request->get('account_id')); - $app['require_connection']($account); - - $route = new RecordHelper\Bridge($app, $app['request']); - $route->grep_records($account->get_api()->acceptable_records()); - $connector = $account->get_api()->get_connector(); - - /** - * check constraints - */ - foreach ($route->get_elements() as $record) { - $datas = $connector->get_upload_datas($request, $record); - $errors = array_merge($errors, $connector->check_upload_constraints($datas, $record)); - } - - if (count($errors) > 0) { - - $params = array( - 'route' => $route - , 'account' => $account - , 'error_message' => _('Request contains invalid datas') - , 'constraint_errors' => $errors - , 'notice_message' => $app['request']->request->get('notice') - , 'adapter_action' => 'upload' - ); - - return $app['twig']->render('prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/upload.html.twig', $params); - } - - foreach ($route->get_elements() as $record) { - $datas = $connector->get_upload_datas($request, $record); - $title = isset($datas["title"]) ? $datas["title"] : ''; - $default_type = $connector->get_default_element_type(); - \Bridge_Element::create($app, $account, $record, $title, \Bridge_Element::STATUS_PENDING, $default_type, $datas); - } - - return $app->redirect('/prod/bridge/adapter/' . $account->get_id() . '/load-records/?notice=' . sprintf(_('%d elements en attente'), count($route->get_elements()))); - })->bind('prod_bridge_do_upload'); + $controllers + ->post('/upload/', 'bridge.controller:doPostUpload') + ->bind('prod_bridge_do_upload'); return $controllers; } + + private function requireConnection(Application $app, \Bridge_Account $account) + { + $app['bridge.account'] = $account; + + if (!$account->get_api()->get_connector()->is_configured()) { + throw new \Bridge_Exception_ApiConnectorNotConfigured("Bridge API Connector is not configured"); + } + if (!$account->get_api()->get_connector()->is_connected()) { + throw new \Bridge_Exception_ApiConnectorNotConnected("Bridge API Connector is not connected"); + } + } + + public function doPostManager(Application $app, Request $request) + { + $route = new RecordHelper\Bridge($app, $request); + $params = array( + 'user_accounts' => \Bridge_Account::get_accounts_by_user($app, $app['authentication']->getUser()), + 'available_apis' => \Bridge_Api::get_availables($app), + 'route' => $route, + 'current_account_id' => '', + ); + + return $app['twig']->render('prod/actions/Bridge/index.html.twig', $params); + } + + public function doGetLogin(Application $app, Request $request, $api_name) + { + $connector = \Bridge_Api::get_connector_by_name($app, $api_name); + + return $app->redirect($connector->get_auth_url()); + } + + public function doGetCallback(Application $app, Request $request, $api_name) + { + $error_message = ''; + try { + $api = \Bridge_Api::get_by_api_name($app, $api_name); + $connector = $api->get_connector(); + $response = $connector->connect(); + $user_id = $connector->get_user_id(); + + try { + $account = \Bridge_Account::load_account_from_distant_id($app, $api, $app['authentication']->getUser(), $user_id); + } catch (\Bridge_Exception_AccountNotFound $e) { + $account = \Bridge_Account::create($app, $api, $app['authentication']->getUser(), $user_id, $connector->get_user_name()); + } + + $settings = $account->get_settings(); + + if (isset($response['auth_token'])) { + $settings->set('auth_token', $response['auth_token']); + } + if (isset($response['refresh_token'])) { + $settings->set('refresh_token', $response['refresh_token']); + } + + $connector->set_auth_settings($settings); + $connector->reconnect(); + } catch (\Exception $e) { + $error_message = $e->getMessage(); + } + + $params = array('error_message' => $error_message); + + return $app['twig']->render('prod/actions/Bridge/callback.html.twig', $params); + } + + public function doGetAccountLogout(Application $app, Request $request, $account_id) + { + $account = \Bridge_Account::load_account($app, $account_id); + $this->requireConnection($app, $account); + $account->get_api()->get_connector()->disconnect(); + + return $app->redirectPath('bridge_load_elements', array( + 'account_id' => $account_id, + 'type' => $account->get_api()->get_connector()->get_default_element_type(), + )); + } + + public function doPostAccountDelete(Application $app, Request $request, $account_id) + { + $success = false; + $message = ''; + try { + $account = \Bridge_Account::load_account($app, $account_id); + + if ($account->get_user()->get_id() !== $app['authentication']->getUser()->get_id()) { + throw new HttpException(403, 'Access forbiden'); + } + + $account->delete(); + $success = true; + } catch (\Bridge_Exception_AccountNotFound $e) { + $message = _('Account is not found.'); + } catch (\Exception $e) { + $message = _('Something went wrong, please contact an administrator'); + } + + return $app->json(array('success' => $success, 'message' => $message)); + } + + public function doGetloadRecords(Application $app, Request $request, $account_id) + { + $page = max((int) $request->query->get('page'), 0); + $quantity = 10; + $offset_start = max(($page - 1) * $quantity, 0); + $account = \Bridge_Account::load_account($app, $account_id); + $elements = \Bridge_Element::get_elements_by_account($app, $account, $offset_start, $quantity); + + $this->requireConnection($app, $account); + + $params = array( + 'adapter_action' => 'load-records' + , 'account' => $account + , 'elements' => $elements + , 'error_message' => $request->query->get('error') + , 'notice_message' => $request->query->get('notice') + ); + + return $app['twig']->render('prod/actions/Bridge/records_list.html.twig', $params); + } + + public function doGetLoadElements(Application $app, Request $request, $account_id, $type) + { + $page = max((int) $request->query->get('page'), 0); + $quantity = 5; + $offset_start = max(($page - 1) * $quantity, 0); + $account = \Bridge_Account::load_account($app, $account_id); + + $this->requireConnection($app, $account); + + $elements = $account->get_api()->list_elements($type, $offset_start, $quantity); + + $params = array( + 'action_type' => $type, + 'adapter_action' => 'load-elements', + 'account' => $account, + 'elements' => $elements, + 'error_message' => $request->query->get('error'), + 'notice_message' => $request->query->get('notice'), + ); + + return $app['twig']->render('prod/actions/Bridge/element_list.html.twig', $params); + } + + public function doGetLoadContainers(Application $app, Request $request, $account_id, $type) + { + $page = max((int) $request->query->get('page'), 0); + $quantity = 5; + $offset_start = max(($page - 1) * $quantity, 0); + $account = \Bridge_Account::load_account($app, $account_id); + + $this->requireConnection($app, $account); + $elements = $account->get_api()->list_containers($type, $offset_start, $quantity); + + $params = array( + 'action_type' => $type, + 'adapter_action' => 'load-containers', + 'account' => $account, + 'elements' => $elements, + 'error_message' => $request->query->get('error'), + 'notice_message' => $request->query->get('notice'), + ); + + return $app['twig']->render('prod/actions/Bridge/element_list.html.twig', $params); + } + + public function doGetAction(Application $app, Request $request, $account_id, $action, $element_type) + { + $account = \Bridge_Account::load_account($app, $account_id); + + $this->requireConnection($app, $account); + $elements = $request->query->get('elements_list', array()); + $elements = is_array($elements) ? $elements : explode(';', $elements); + + $destination = $request->query->get('destination'); + $route_params = array(); + $class = $account->get_api()->get_connector()->get_object_class_from_type($element_type); + + switch ($action) { + case 'createcontainer': + break; + + case 'modify': + if (count($elements) != 1) { + return $app->redirectPath('bridge_load_elements', array( + 'account_id' => $account_id, + 'type' => $element_type, + 'page' => '', + 'error' => _('Vous ne pouvez pas editer plusieurs elements simultanement'), + )); + } + foreach ($elements as $element_id) { + if ($class === \Bridge_Api_Interface::OBJECT_CLASS_ELEMENT) { + $route_params = array('element' => $account->get_api()->get_element_from_id($element_id, $element_type)); + } + if ($class === \Bridge_Api_Interface::OBJECT_CLASS_CONTAINER) { + $route_params = array('element' => $account->get_api()->get_container_from_id($element_id, $element_type)); + } + } + break; + + case 'moveinto': + $route_params = array('containers' => $account->get_api()->list_containers($destination, 0, 0)); + break; + + case 'deleteelement': + break; + + default: + throw new \Exception(_('Vous essayez de faire une action que je ne connais pas !')); + break; + } + + $params = array( + 'account' => $account, + 'destination' => $destination, + 'element_type' => $element_type, + 'action' => $action, + 'constraint_errors' => null, + 'adapter_action' => $action, + 'elements' => $elements, + 'error_message' => $request->query->get('error'), + 'notice_message' => $request->query->get('notice'), + ); + + $params = array_merge($params, $route_params); + $template = 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/' . $element_type . '_' . $action . ($destination ? '_' . $destination : '') . '.html.twig'; + + return $app['twig']->render($template, $params); + } + + public function doPostAction(Application $app, Request $request, $account_id, $action, $element_type) + { + $account = \Bridge_Account::load_account($app, $account_id); + + $this->requireConnection($app, $account); + + $elements = $request->request->get('elements_list', array()); + $elements = is_array($elements) ? $elements : explode(';', $elements); + + $destination = $request->request->get('destination'); + + $class = $account->get_api()->get_connector()->get_object_class_from_type($element_type); + $html = ''; + switch ($action) { + case 'modify': + if (count($elements) != 1) { + return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?elements_list=' . implode(';', $elements) . '&error=' . _('Vous ne pouvez pas editer plusieurs elements simultanement')); + } + try { + foreach ($elements as $element_id) { + $datas = $account->get_api()->get_connector()->get_update_datas($request); + $errors = $account->get_api()->get_connector()->check_update_constraints($datas); + } + + if (count($errors) > 0) { + $params = array( + 'element' => $account->get_api()->get_element_from_id($element_id, $element_type), + 'account' => $account, + 'destination' => $destination, + 'element_type' => $element_type, + 'action' => $action, + 'elements' => $elements, + 'adapter_action' => $action, + 'error_message' => _('Request contains invalid datas'), + 'constraint_errors' => $errors, + 'notice_message' => $request->request->get('notice'), + ); + + $template = 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/' . $element_type . '_' . $action . ($destination ? '_' . $destination : '') . '.html.twig'; + + return $app['twig']->render($template, $params); + } + + foreach ($elements as $element_id) { + $datas = $account->get_api()->get_connector()->get_update_datas($request); + $account->get_api()->update_element($element_type, $element_id, $datas); + } + } catch (\Exception $e) { + return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?elements_list[]=' . $element_id . '&error=' . get_class($e) . ' : ' . $e->getMessage()); + } + + return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/?page=&update=success#anchor'); + case 'createcontainer': + try { + $container_type = $request->request->get('f_container_type'); + + $account->get_api()->create_container($container_type, $request); + } catch (\Exception $e) { + return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . ' : ' . $e->getMessage()); + } + + return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/?page=&update=success#anchor'); + case 'moveinto': + try { + $container_id = $request->request->get('container_id'); + foreach ($elements as $element_id) { + $account->get_api()->add_element_to_container($element_type, $element_id, $destination, $container_id); + } + } catch (\Exception $e) { + return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . ' : ' . $e->getMessage()); + } + + return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-containers/' . $destination . '/?page=&update=success#anchor'); + case 'deleteelement': + try { + foreach ($elements as $element_id) { + $account->get_api()->delete_object($element_type, $element_id); + } + } catch (\Exception $e) { + return $app->redirect('/prod/bridge/action/' . $account_id . '/' . $action . '/' . $element_type . '/?error=' . get_class($e) . $e->getMessage()); + } + + return $app->redirect('/prod/bridge/adapter/' . $account_id . '/load-' . $class . 's/' . $element_type . '/'); + default: + throw new \Exception('Unknown action'); + break; + } + + return new Response($html); + } + + public function doGetUpload(Application $app, Request $request) + { + $account = \Bridge_Account::load_account($app, $request->query->get('account_id')); + $this->requireConnection($app, $account); + + $route = new RecordHelper\Bridge($app, $request); + + $route->grep_records($account->get_api()->acceptable_records()); + + $params = array( + 'route' => $route, + 'account' => $account, + 'error_message' => $request->query->get('error'), + 'notice_message' => $request->query->get('notice'), + 'constraint_errors' => null, + 'adapter_action' => 'upload', + ); + + return $app['twig']->render( + 'prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/upload.html.twig', $params + ); + } + + public function doPostUpload(Application $app, Request $request) + { + $errors = array(); + $account = \Bridge_Account::load_account($app, $request->request->get('account_id')); + $this->requireConnection($app, $account); + + $route = new RecordHelper\Bridge($app, $request); + $route->grep_records($account->get_api()->acceptable_records()); + $connector = $account->get_api()->get_connector(); + + // check constraints + foreach ($route->get_elements() as $record) { + $datas = $connector->get_upload_datas($request, $record); + $errors = array_merge($errors, $connector->check_upload_constraints($datas, $record)); + } + + if (count($errors) > 0) { + $params = array( + 'route' => $route, + 'account' => $account, + 'error_message' => _('Request contains invalid datas'), + 'constraint_errors' => $errors, + 'notice_message' => $request->request->get('notice'), + 'adapter_action' => 'upload', + ); + + return $app['twig']->render('prod/actions/Bridge/' . $account->get_api()->get_connector()->get_name() . '/upload.html.twig', $params); + } + + foreach ($route->get_elements() as $record) { + $datas = $connector->get_upload_datas($request, $record); + $title = isset($datas["title"]) ? $datas["title"] : ''; + $default_type = $connector->get_default_element_type(); + \Bridge_Element::create($app, $account, $record, $title, \Bridge_Element::STATUS_PENDING, $default_type, $datas); + } + + return $app->redirect('/prod/bridge/adapter/' . $account->get_id() . '/load-records/?notice=' . sprintf(_('%d elements en attente'), count($route->get_elements()))); + } } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php index 18ccc632b6..fd71ac62a6 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/DoDownload.php @@ -15,6 +15,7 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class DoDownload implements ControllerProviderInterface { @@ -91,11 +92,7 @@ class DoDownload implements ControllerProviderInterface */ public function prepareDownload(Application $app, Request $request, $token) { - try { - $datas = $app['tokens']->helloToken($token); - } catch (\Exception_NotFound $e) { - $app->abort(404, 'Invalid token'); - } + $datas = $app['tokens']->helloToken($token); if (false === $list = @unserialize((string) $datas['datas'])) { $app->abort(500, 'Invalid datas'); @@ -140,11 +137,7 @@ class DoDownload implements ControllerProviderInterface */ public function downloadDocuments(Application $app, Request $request, $token) { - try { - $datas = $app['tokens']->helloToken($token); - } catch (\Exception_NotFound $e) { - $app->abort(404, 'Invalid token'); - } + $datas = $app['tokens']->helloToken($token); if (false === $list = @unserialize((string) $datas['datas'])) { $app->abort(500, 'Invalid datas'); @@ -160,7 +153,7 @@ class DoDownload implements ControllerProviderInterface $mime = $subdef['mime']; $list['complete'] = true; } else { - $exportFile = __DIR__ . '/../../../../../tmp/download/' . $datas['value'] . '.zip'; + $exportFile = $app['root.path'] . '/tmp/download/' . $datas['value'] . '.zip'; $mime = 'application/zip'; } @@ -202,7 +195,7 @@ class DoDownload implements ControllerProviderInterface { try { $datas = $app['tokens']->helloToken($token); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { return $app->json(array( 'success' => false, 'message' => 'Invalid token' @@ -225,7 +218,7 @@ class DoDownload implements ControllerProviderInterface $app, $token, $list, - sprintf('%s/../../../../../tmp/download/%s.zip', __DIR__, $datas['value']) // Dest file + sprintf($app['root.path'] . '/tmp/download/%s.zip', $datas['value']) // Dest file ); return $app->json(array( diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Feed.php b/lib/Alchemy/Phrasea/Controller/Prod/Feed.php index 7cce1ae8b5..36ff032d48 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Feed.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Feed.php @@ -16,6 +16,8 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -79,7 +81,7 @@ class Feed implements ControllerProviderInterface $entry = \Feed_Entry_Adapter::load_from_id($app, $id); if (!$entry->is_publisher($app['authentication']->getUser())) { - throw new \Exception_UnauthorizedAction(); + throw new AccessDeniedHttpException(); } $feeds = \Feed_Collection::load_all($app, $app['authentication']->getUser()); @@ -102,7 +104,7 @@ class Feed implements ControllerProviderInterface $entry = \Feed_Entry_Adapter::load_from_id($app, $id); if (!$entry->is_publisher($app['authentication']->getUser())) { - throw new \Exception_UnauthorizedAction(); + throw new AccessDeniedHttpException(); } $title = $request->request->get('title'); @@ -120,12 +122,12 @@ class Feed implements ControllerProviderInterface if ($current_feed_id != $new_feed_id) { try { $new_feed = \Feed_Adapter::load_with_user($app, $app['authentication']->getUser(), $new_feed_id); - } catch (\Exception_NotFound $e) { - throw new \Exception_Forbidden('You have no access to this feed'); + } catch (NotFoundHttpException $e) { + throw new AccessDeniedHttpException('You have no access to this feed'); } if (!$new_feed->is_publisher($app['authentication']->getUser())) { - throw new \Exception_Forbidden('You are not publisher of this feed'); + throw new AccessDeniedHttpException('You are not publisher of this feed'); } $entry->set_feed($new_feed); @@ -151,10 +153,10 @@ class Feed implements ControllerProviderInterface } catch (\Exception_Feed_EntryNotFound $e) { $app['phraseanet.appbox']->get_connection()->rollBack(); $datas['message'] = _('Feed entry not found'); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $app['phraseanet.appbox']->get_connection()->rollBack(); $datas['message'] = _('Feed not found'); - } catch (\Exception_Forbidden $e) { + } catch (AccessDeniedHttpException $e) { $app['phraseanet.appbox']->get_connection()->rollBack(); $datas['message'] = _('You are not authorized to access this feed'); } catch (\Exception $e) { @@ -178,7 +180,7 @@ class Feed implements ControllerProviderInterface if (!$entry->is_publisher($app['authentication']->getUser()) && $entry->get_feed()->is_owner($app['authentication']->getUser()) === false) { - throw new \Exception_UnauthorizedAction(_('Action Forbidden : You are not the publisher')); + throw new AccessDeniedHttpException(_('Action Forbidden : You are not the publisher')); } $entry->delete(); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/MustacheLoader.php b/lib/Alchemy/Phrasea/Controller/Prod/MustacheLoader.php index 80ccb8f622..ac7fb7a018 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/MustacheLoader.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/MustacheLoader.php @@ -14,6 +14,8 @@ namespace Alchemy\Phrasea\Controller\Prod; use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -31,13 +33,13 @@ class MustacheLoader implements ControllerProviderInterface $template_name = $request->query->get('template'); if (!preg_match('/^[a-zA-Z0-9-_]+$/', $template_name)) { - throw new \Exception_BadRequest('Wrong template name : ' . $template_name); + throw new BadRequestHttpException('Wrong template name : ' . $template_name); } $template_path = realpath(__DIR__ . '/../../../../../templates/web/Mustache/Prod/' . $template_name . '.Mustache.html'); if (!file_exists($template_path)) { - throw new \Exception_NotFound('Template does not exists : ' . $template_path); + throw new NotFoundHttpException('Template does not exists : ' . $template_path); } return new \Symfony\Component\HttpFoundation\Response(file_get_contents($template_path)); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Order.php b/lib/Alchemy/Phrasea/Controller/Prod/Order.php index 4771b3260e..a1943c348a 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Order.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Order.php @@ -255,11 +255,7 @@ class Order implements ControllerProviderInterface */ public function displayOneOrder(Application $app, Request $request, $order_id) { - try { - $order = new \set_order($app, $order_id); - } catch (\Exception_NotFound $e) { - $app->abort(404); - } + $order = new \set_order($app, $order_id); return $app['twig']->render('prod/orders/order_item.html.twig', array( 'order' => $order @@ -278,11 +274,7 @@ class Order implements ControllerProviderInterface { $success = false; - try { - $order = new \set_order($app, $order_id); - } catch (\Exception_NotFound $e) { - $app->abort(404); - } + $order = new \set_order($app, $order_id); try { $order->send_elements($app, $request->request->get('elements', array()), !!$request->request->get('force', false)); @@ -317,11 +309,7 @@ class Order implements ControllerProviderInterface { $success = false; - try { - $order = new \set_order($app, $order_id); - } catch (\Exception_NotFound $e) { - $app->abort(404); - } + $order = new \set_order($app, $order_id); try { $order->deny_elements($request->request->get('elements', array())); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Push.php b/lib/Alchemy/Phrasea/Controller/Prod/Push.php index 540b0d5825..df58094346 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Push.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Push.php @@ -17,6 +17,7 @@ use Alchemy\Phrasea\Helper\Record as RecordHelper; use Alchemy\Phrasea\Controller\Exception as ControllerException; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -370,7 +371,7 @@ class Push implements ControllerProviderInterface try { $Participant = $Validation->getParticipant($participant_user, $app); continue; - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Root.php b/lib/Alchemy/Phrasea/Controller/Prod/Root.php index 74027111f8..c4a278907f 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Root.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Root.php @@ -32,7 +32,6 @@ class Root implements ControllerProviderInterface $controllers = $app['controllers_factory']; $controllers->before(function(Request $request) use ($app) { - if (!$app['authentication']->isAuthenticated() && null !== $request->query->get('nolog')) { return $app->redirectPath('login_authenticate_as_guest'); } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Story.php b/lib/Alchemy/Phrasea/Controller/Prod/Story.php index be3de67a06..7661a2d360 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Story.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Story.php @@ -17,6 +17,7 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * @@ -43,7 +44,7 @@ class Story implements ControllerProviderInterface $collection = \collection::get_from_base_id($app, $request->request->get('base_id')); if (!$app['authentication']->getUser()->ACL()->has_right_on_base($collection->get_base_id(), 'canaddrecord')) { - throw new \Exception_Forbidden('You can not create a story on this collection'); + throw new AccessDeniedHttpException('You can not create a story on this collection'); } $Story = \record_adapter::createStory($app, $collection); @@ -121,7 +122,7 @@ class Story implements ControllerProviderInterface $Story = new \record_adapter($app, $sbas_id, $record_id); if (!$app['authentication']->getUser()->ACL()->has_right_on_base($Story->get_base_id(), 'canmodifrecord')) - throw new \Exception_Forbidden('You can not add document to this Story'); + throw new AccessDeniedHttpException('You can not add document to this Story'); $n = 0; @@ -154,7 +155,7 @@ class Story implements ControllerProviderInterface $record = new \record_adapter($app, $child_sbas_id, $child_record_id); if (!$app['authentication']->getUser()->ACL()->has_right_on_base($Story->get_base_id(), 'canmodifrecord')) - throw new \Exception_Forbidden('You can not add document to this Story'); + throw new AccessDeniedHttpException('You can not add document to this Story'); $Story->removeChild($record); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Upload.php b/lib/Alchemy/Phrasea/Controller/Prod/Upload.php index a5bb086979..8e96e4b69c 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Upload.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Upload.php @@ -19,6 +19,8 @@ use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\File\UploadedFile; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; /** * Upload controller collection @@ -159,27 +161,27 @@ class Upload implements ControllerProviderInterface ); if (null === $request->files->get('files')) { - throw new \Exception_BadRequest('Missing file parameter'); + throw new BadRequestHttpException('Missing file parameter'); } if (count($request->files->get('files')) > 1) { - throw new \Exception_BadRequest('Upload is limited to 1 file per request'); + throw new BadRequestHttpException('Upload is limited to 1 file per request'); } $base_id = $request->request->get('base_id'); if (!$base_id) { - throw new \Exception_BadRequest('Missing base_id parameter'); + throw new BadRequestHttpException('Missing base_id parameter'); } if (!$app['authentication']->getUser()->ACL()->has_right_on_base($base_id, 'canaddrecord')) { - throw new \Exception_Forbidden('User is not allowed to add record on this collection'); + throw new AccessDeniedHttpException('User is not allowed to add record on this collection'); } $file = current($request->files->get('files')); if (!$file->isValid()) { - throw new \Exception_BadRequest('Uploaded file is invalid'); + throw new BadRequestHttpException('Uploaded file is invalid'); } try { diff --git a/lib/Alchemy/Phrasea/Controller/Prod/UsrLists.php b/lib/Alchemy/Phrasea/Controller/Prod/UsrLists.php index c543431ddd..9f47f7f9bc 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/UsrLists.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/UsrLists.php @@ -19,6 +19,7 @@ use Silex\ControllerProviderInterface; use Alchemy\Phrasea\Controller\Exception as ControllerException; use Symfony\Component\HttpFoundation\Request; use Doctrine\Common\Collections\ArrayCollection; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; /** * @@ -477,9 +478,9 @@ class UsrLists implements ControllerProviderInterface ); if (!$app['request']->request->get('role')) - throw new \Exception_BadRequest('Missing role parameter'); + throw new BadRequestHttpException('Missing role parameter'); elseif (!in_array($app['request']->request->get('role'), $availableRoles)) - throw new \Exception_BadRequest('Role is invalid'); + throw new BadRequestHttpException('Role is invalid'); try { $repository = $app['EM']->getRepository('\Entities\UsrList'); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/WorkZone.php b/lib/Alchemy/Phrasea/Controller/Prod/WorkZone.php index 19b658cfd4..89b5ce7c6f 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/WorkZone.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/WorkZone.php @@ -16,6 +16,9 @@ use Silex\Application; use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\Request; use Alchemy\Phrasea\Helper\WorkZone as WorkzoneHelper; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; +use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -121,7 +124,7 @@ class WorkZone implements ControllerProviderInterface public function attachStories(Application $app, Request $request) { if (!$request->request->get('stories')) { - throw new \Exception_BadRequest(); + throw new BadRequestHttpException('Missing parameters stories'); } $StoryWZRepo = $app['EM']->getRepository('\Entities\StoryWZ'); @@ -139,7 +142,7 @@ class WorkZone implements ControllerProviderInterface } if (!$app['authentication']->getUser()->ACL()->has_access_to_base($Story->get_base_id())) { - throw new \Exception_Forbidden('You do not have access to this Story'); + throw new AccessDeniedHttpException('You do not have access to this Story'); } if ($StoryWZRepo->findUserStory($app, $app['authentication']->getUser(), $Story)) { @@ -205,7 +208,7 @@ class WorkZone implements ControllerProviderInterface $StoryWZ = $repository->findUserStory($app, $app['authentication']->getUser(), $Story); if (!$StoryWZ) { - throw new \Exception_NotFound('Story not found'); + throw new NotFoundHttpException('Story not found'); } $app['EM']->remove($StoryWZ); diff --git a/lib/Alchemy/Phrasea/Controller/RecordsRequest.php b/lib/Alchemy/Phrasea/Controller/RecordsRequest.php index 970c929190..64b8010168 100644 --- a/lib/Alchemy/Phrasea/Controller/RecordsRequest.php +++ b/lib/Alchemy/Phrasea/Controller/RecordsRequest.php @@ -15,6 +15,7 @@ use Entities\Basket; use Doctrine\Common\Collections\ArrayCollection; use Alchemy\Phrasea\Application; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class RecordsRequest extends ArrayCollection { @@ -223,7 +224,7 @@ class RecordsRequest extends ArrayCollection $record = new \record_adapter($app, (int) $basrec[0], (int) $basrec[1]); $received[$record->get_serialize_key()] = $record; unset($record); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { continue; } } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Account.php b/lib/Alchemy/Phrasea/Controller/Root/Account.php index 39c47926f9..39bb3c2cf3 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Account.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Account.php @@ -22,6 +22,7 @@ use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class Account implements ControllerProviderInterface { @@ -221,7 +222,7 @@ class Account implements ControllerProviderInterface ); $account->set_revoked((bool) $request->query->get('revoke'), false); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $error = true; } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Developers.php b/lib/Alchemy/Phrasea/Controller/Root/Developers.php index ae15b62746..d86c04c0b5 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Developers.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Developers.php @@ -16,6 +16,7 @@ use Silex\ControllerProviderInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * @@ -189,7 +190,7 @@ class Developers implements ControllerProviderInterface try { $clientApp = new \API_OAuth2_Application($app, $id); $clientApp->delete(); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $error = true; } @@ -220,7 +221,7 @@ class Developers implements ControllerProviderInterface } else { $error = true; } - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $error = true; } @@ -283,7 +284,7 @@ class Developers implements ControllerProviderInterface try { $clientApp = new \API_OAuth2_Application($app, $id); $clientApp->set_grant_password((bool) $request->request->get('grant', false)); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $error = true; } @@ -368,7 +369,7 @@ class Developers implements ControllerProviderInterface { try { $client = new \API_OAuth2_Application($app, $id); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $app->abort(404); } diff --git a/lib/Alchemy/Phrasea/Controller/Root/Login.php b/lib/Alchemy/Phrasea/Controller/Root/Login.php index 58b47aaecd..a59081bf46 100644 --- a/lib/Alchemy/Phrasea/Controller/Root/Login.php +++ b/lib/Alchemy/Phrasea/Controller/Root/Login.php @@ -474,7 +474,7 @@ class Login implements ControllerProviderInterface try { $datas = $app['tokens']->helloToken($code); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { $app->addFlash('error', _('Invalid unlock link.')); return $app->redirectPath('homepage'); @@ -795,7 +795,7 @@ class Login implements ControllerProviderInterface try { $token = $this->app['tokens']->getValidationToken($participantId, $basketId); - } catch (\Exception_NotFound $e) { + } catch (NotFoundHttpException $e) { continue; } diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiExceptionHandlerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiExceptionHandlerSubscriber.php new file mode 100644 index 0000000000..c7374e0b28 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiExceptionHandlerSubscriber.php @@ -0,0 +1,75 @@ +app = $app; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => array('onSilexError', 0), + ); + } + + public function onSilexError(GetResponseForExceptionEvent $event) + { + $headers = array(); + $e = $event->getException(); + + if ($e instanceof \API_V1_exception_methodnotallowed) { + $code = \API_V1_result::ERROR_METHODNOTALLOWED; + } elseif ($e instanceof MethodNotAllowedHttpException) { + $code = \API_V1_result::ERROR_METHODNOTALLOWED; + } elseif ($e instanceof \API_V1_exception_badrequest) { + $code = \API_V1_result::ERROR_BAD_REQUEST; + } elseif ($e instanceof \API_V1_exception_forbidden) { + $code = \API_V1_result::ERROR_FORBIDDEN; + } elseif ($e instanceof \API_V1_exception_unauthorized) { + $code = \API_V1_result::ERROR_UNAUTHORIZED; + } elseif ($e instanceof \API_V1_exception_internalservererror) { + $code = \API_V1_result::ERROR_INTERNALSERVERERROR; + } elseif ($e instanceof NotFoundHttpException) { + $code = \API_V1_result::ERROR_NOTFOUND; + } else { + $code = \API_V1_result::ERROR_INTERNALSERVERERROR; + } + + if ($e instanceof HttpException) { + $headers = $e->getHeaders(); + } + + $result = $this->app['api']->get_error_message($event->getRequest(), $code, $e->getMessage()); + $response = $result->get_response(); + $response->headers->set('X-Status-Code', $result->get_http_code()); + + foreach ($headers as $key => $value) { + $response->headers->set($key, $value); + } + + $event->setResponse($response); + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiOauth2ErrorsSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiOauth2ErrorsSubscriber.php new file mode 100644 index 0000000000..f0ea27a88e --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/ApiOauth2ErrorsSubscriber.php @@ -0,0 +1,64 @@ +handler = $handler; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::EXCEPTION => array('onSilexError', 20), + ); + } + + public function onSilexError(GetResponseForExceptionEvent $event) + { + $request = $event->getRequest(); + + if (0 !== strpos($request->getPathInfo(), '/api/oauthv2')) { + return; + } + + $e = $event->getException(); + + $code = 500; + $msg = _('Whoops, looks like something went wrong.'); + $headers = array(); + + if ($e instanceof HttpExceptionInterface) { + $headers = $e->getHeaders(); + $msg = $e->getMessage(); + $code = $e->getStatusCode(); + } + + if (isset($headers['content-type']) && $headers['content-type'] == 'application/json') { + $msg = json_encode(array('msg' => $msg, 'code' => $code)); + $event->setResponse(new Response($msg, $code, $headers)); + } else { + $event->setResponse($this->handler->createResponseBasedOnRequest($event->getRequest(), $event->getException())); + } + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeExceptionSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeExceptionSubscriber.php new file mode 100644 index 0000000000..b0ceeb3d09 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/BridgeExceptionSubscriber.php @@ -0,0 +1,79 @@ +app = $app; + } + + public function onSilexError(GetResponseForExceptionEvent $event) + { + if (!$event->getException() instanceof \Bridge_Exception) { + return; + } + + $e = $event->getException(); + $request = $event->getRequest(); + + $params = array( + 'account' => null, + 'elements' => array(), + 'message' => $e->getMessage(), + 'error_message' => null, + 'notice_message' => null, + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'r_method' => $request->getMethod(), + 'r_action' => $request->getRequestUri(), + 'r_parameters' => ($request->getMethod() == 'GET' ? array() : $request->request->all()), + ); + + if ($e instanceof \Bridge_Exception_ApiConnectorNotConfigured) { + $params = array_replace($params, array('account' => $this->app['bridge.account'])); + $response = new Response($this->app['twig']->render('/prod/actions/Bridge/notconfigured.html.twig', $params), 200, array('X-Status-Code' => 200)); + } elseif ($e instanceof \Bridge_Exception_ApiConnectorNotConnected) { + $params = array_replace($params, array('account' => $this->app['bridge.account'])); + $response = new Response($this->app['twig']->render('/prod/actions/Bridge/disconnected.html.twig', $params), 200, array('X-Status-Code' => 200)); + } elseif ($e instanceof \Bridge_Exception_ApiConnectorAccessTokenFailed) { + $params = array_replace($params, array('account' => $this->app['bridge.account'])); + $response = new Response($this->app['twig']->render('/prod/actions/Bridge/disconnected.html.twig', $params), 200, array('X-Status-Code' => 200)); + } elseif ($e instanceof \Bridge_Exception_ApiDisabled) { + $params = array_replace($params, array('api' => $e->get_api())); + $response = new Response($this->app['twig']->render('/prod/actions/Bridge/deactivated.html.twig', $params), 200, array('X-Status-Code' => 200)); + } else { + $response = new Response($this->app['twig']->render('/prod/actions/Bridge/error.html.twig', $params), 200, array('X-Status-Code' => 200)); + } + + $response->headers->set('Phrasea-StatusCode', 200); + + $event->setResponse($response); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(KernelEvents::EXCEPTION => array('onSilexError', 20)); + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php new file mode 100644 index 0000000000..76e11186bc --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/DebuggerSubscriber.php @@ -0,0 +1,59 @@ +app = $app; + } + + public static function getSubscribedEvents() + { + return array( + KernelEvents::REQUEST => array( + array('checkIp', 255), + ), + ); + } + + public function checkIp(GetResponseEvent $event) + { + if (Application::ENV_DEV !== $this->app->getEnvironment()) { + return; + } + + if (isset($this->app['phraseanet.configuration']['debugger']) + && isset($this->app['phraseanet.configuration']['debugger']['allowed-ips'])) { + + $allowedIps = $this->app['phraseanet.configuration']['debugger']['allowed-ips']; + $allowedIps = is_array($allowedIps) ? $allowedIps : array($allowedIps); + } else { + $allowedIps = array(); + } + + $ips = array_merge(array('127.0.0.1', 'fe80::1', '::1'), $allowedIps); + + if (!in_array($event->getRequest()->getClientIp(), $ips)) { + throw new AccessDeniedHttpException('You are not allowed to access this file. Check index_dev.php for more information.'); + } + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/FirewallSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FirewallSubscriber.php new file mode 100644 index 0000000000..f28c2662a9 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/FirewallSubscriber.php @@ -0,0 +1,50 @@ + array('onKernelResponse', 0), + KernelEvents::EXCEPTION => array('onSilexError', 20), + ); + } + + public function onKernelResponse(FilterResponseEvent $event) + { + if ($event->getResponse()->headers->has('X-Phraseanet-Redirect')) { + $event->getResponse()->headers->remove('X-Phraseanet-Redirect'); + } + } + + public function onSilexError(GetResponseForExceptionEvent $event) + { + $e = $event->getException(); + + if ($e instanceof HttpExceptionInterface) { + $headers = $e->getHeaders(); + + if (isset($headers['X-Phraseanet-Redirect'])) { + $event->setResponse(new RedirectResponse($headers['X-Phraseanet-Redirect'], 302, array('X-Status-Code' => 302))); + } + } + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php new file mode 100644 index 0000000000..d21e902e69 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/JsonRequestSubscriber.php @@ -0,0 +1,46 @@ +getException(); + $request = $event->getRequest(); + + if ((0 !== strpos($request->getPathInfo(), '/admin/') + || 0 === strpos($request->getPathInfo(), '/admin/collection/') + || 0 === strpos($request->getPathInfo(), '/admin/databox/')) + && $request->getRequestFormat() == 'json') { + $datas = array( + 'success' => false, + 'message' => $exception->getMessage(), + ); + + $event->setResponse(new JsonResponse($datas, 200, array('X-Status-Code' => 200))); + } + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(KernelEvents::EXCEPTION => array('onSilexError', 10)); + } +} diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/Logout.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/LogoutSubscriber.php similarity index 92% rename from lib/Alchemy/Phrasea/Core/Event/Subscriber/Logout.php rename to lib/Alchemy/Phrasea/Core/Event/Subscriber/LogoutSubscriber.php index 51ab856ac5..74c4372ff0 100644 --- a/lib/Alchemy/Phrasea/Core/Event/Subscriber/Logout.php +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/LogoutSubscriber.php @@ -15,7 +15,7 @@ use Alchemy\Phrasea\Core\PhraseaEvents; use Alchemy\Phrasea\Core\Event\LogoutEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -class Logout implements EventSubscriberInterface +class LogoutSubscriber implements EventSubscriberInterface { public function onLogout(LogoutEvent $event) { diff --git a/lib/Alchemy/Phrasea/Core/Event/Subscriber/PhraseaExceptionHandlerSubscriber.php b/lib/Alchemy/Phrasea/Core/Event/Subscriber/PhraseaExceptionHandlerSubscriber.php new file mode 100644 index 0000000000..24973b0a73 --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/Event/Subscriber/PhraseaExceptionHandlerSubscriber.php @@ -0,0 +1,51 @@ +enabled = true; + $this->handler = $handler; + } + + public function disable() + { + $this->enabled = false; + } + + public function onSilexError(GetResponseForExceptionEvent $event) + { + if (!$this->enabled) { + return; + } + + $event->setResponse($this->handler->createResponseBasedOnRequest($event->getRequest(), $event->getException())); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return array(KernelEvents::EXCEPTION => array('onSilexError', 0)); + } +} diff --git a/lib/Alchemy/Phrasea/Core/PhraseaExceptionHandler.php b/lib/Alchemy/Phrasea/Core/PhraseaExceptionHandler.php new file mode 100644 index 0000000000..82764c562f --- /dev/null +++ b/lib/Alchemy/Phrasea/Core/PhraseaExceptionHandler.php @@ -0,0 +1,143 @@ +getStatusCode(): + $title = _('Sorry, the page you are looking for could not be found.'); + break; + case 403 === $exception->getStatusCode(): + $title = _('Sorry, you do have access to the page you are looking for.'); + break; + case 500 === $exception->getStatusCode(): + $title = _('Whoops, looks like something went wrong.'); + break; + case isset(Response::$statusTexts[$exception->getStatusCode()]): + $title = $exception->getStatusCode() . ' : ' . Response::$statusTexts[$exception->getStatusCode()]; + break; + default: + $title = 'Whoops, looks like something went wrong.'; + } + + $content = parent::getContent($exception); + $start = strpos($content, ''); + + $content = '