diff --git a/.bowerrc b/.bowerrc index 764e156e8a..bb6d6aafdb 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,3 +1,4 @@ { - "directory" : "www/bower_components" + "directory" : "www/bower_components", + "timeout": 1200000 } diff --git a/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php b/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php index 85df3351cf..04b0f00612 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/ShareController.php @@ -23,44 +23,71 @@ class ShareController extends Controller */ public function shareRecord($base_id, $record_id) { - $outputVars = [ - 'isAvailable' => false, - 'preview' => [ - 'permalinkUrl' => '', - 'permaviewUrl' => '', - 'embedUrl' => '', - 'width' => '', - 'height' => '' - ] - ]; $record = new \record_adapter($this->app, \phrasea::sbasFromBas($this->app, $base_id), $record_id); - if (!$this->getAclForUser()->has_access_to_subdef($record, 'preview')) { - $this->app->abort(403); - } + //get list of subdefs + $subdefs = $record->get_subdefs(); - $preview = $record->get_preview(); + $databoxSubdefs = $record->getDatabox()->get_subdef_structure()->getSubdefGroup($record->getType()); + $acl = $this->getAclForUser(); + $subdefList = []; + $defaultKey = null; + foreach ($subdefs as $subdef) { + $subdefName = $subdef->get_name(); + if ($subdefName == 'document') { + if (!$acl->has_right_on_base($record->getBaseId(), \ACL::CANDWNLDHD)) { + continue; + } + $label = $this->app->trans('prod::tools: document'); + } + elseif ($databoxSubdefs->hasSubdef($subdefName)) { + if (!$acl->has_access_to_subdef($record, $subdefName)) { + continue; + } + $label = $databoxSubdefs->getSubdef($subdefName)->get_label($this->app['locale']); + } + else { + // this subdef does no exists anymore in databox structure ? + continue; // don't publish it + } + $value = $subdef->get_name(); + $preview = $record->get_subdef($value); + $defaultKey = $value; // will set a default option if neither preview,thumbnail or document is present - if (null !== $previewLink = $preview->get_permalink()) { - $permalinkUrl = $previewLink->get_url(); - $permaviewUrl = $previewLink->get_page(); - $previewWidth = $preview->get_width(); - $previewHeight = $preview->get_height(); - $embedUrl = $this->app->url('alchemy_embed_view', ['url' => (string)$permalinkUrl]); - - $outputVars = [ - 'isAvailable' => true, - 'preview' => [ + if ( ($previewLink = $preview->get_permalink()) !== null ) { + $permalinkUrl = $previewLink->get_url()->__toString(); + $permaviewUrl = $previewLink->get_page(); + $previewWidth = $preview->get_width(); + $previewHeight = $preview->get_height(); + $embedUrl = $this->app->url('alchemy_embed_view', ['url' => (string)$permalinkUrl]); + $previewData = [ + 'label' => $label, 'permalinkUrl' => $permalinkUrl, 'permaviewUrl' => $permaviewUrl, - 'embedUrl' => $embedUrl, - 'width' => $previewWidth, - 'height' => $previewHeight - ] - ]; + 'embedUrl' => $embedUrl, + 'width' => $previewWidth, + 'height' => $previewHeight + ]; + $subdefList[$value] = $previewData; + } } + // candidates as best default selected option + foreach(["preview", "thumbnail", "document"] as $k) { + if (array_key_exists($k, $subdefList)) { + $defaultKey = $k; + break; + } + } + // if no subdef was sharable, subdefList is empty and defaultKey is null + // the twig MUST handle that + $outputVars = [ + 'isAvailable' => !empty($subdefList), + 'subdefList' => $subdefList, + 'defaultKey' => $defaultKey + ]; + return $this->renderResponse('prod/Share/record.html.twig', $outputVars); } } diff --git a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Share.php b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Share.php index e4e58e53b2..d2f1901814 100644 --- a/lib/Alchemy/Phrasea/ControllerProvider/Prod/Share.php +++ b/lib/Alchemy/Phrasea/ControllerProvider/Prod/Share.php @@ -47,10 +47,22 @@ class Share implements ControllerProviderInterface, ServiceProviderInterface $controllers->get('/record/{base_id}/{record_id}/', 'controller.prod.share:shareRecord') ->before(function (Request $request) use ($app, $firewall) { - $firewall->requireRightOnSbas( - \phrasea::sbasFromBas($app, $request->attributes->get('base_id')), - \ACL::BAS_CHUPUB - ); + $socialTools = $app['conf']->get(['registry', 'actions', 'social-tools']); + if($socialTools === "all") { + return; + } + elseif($socialTools === "none") { + $app->abort(403, 'social tools disabled'); + } + elseif($socialTools === "publishers") { + $firewall->requireRightOnSbas( + \phrasea::sbasFromBas($app, $request->attributes->get('base_id')), + \ACL::BAS_CHUPUB + ); + } + else { + throw new \Exception("bad value \"" . $socialTools . "\" for social tools"); + } }) ->bind('share_record'); diff --git a/templates/web/prod/Share/record.html.twig b/templates/web/prod/Share/record.html.twig index 24c26fb200..477f0fe192 100644 --- a/templates/web/prod/Share/record.html.twig +++ b/templates/web/prod/Share/record.html.twig @@ -1,59 +1,122 @@ {% if not isAvailable %}

{{ 'No permalink available.' | trans }}

{% else %} - {% if preview.permalinkUrl is not empty %} + {% if subdefList is not empty %} + {% set defKey = defaultKey %}
-
-

- - - {% trans %}Send to Twitter{% endtrans %} - -

-

- - - {% trans %}Send to Facebook{% endtrans %} - -

- -
-
- - - -

- {{ 'previewLinkLabel' | trans }}    - {{ 'copyClipboardLabel' | trans }} -

-
-
- - -

- {{ 'previewLinkLabel' | trans }}    - {{ 'copyClipboardLabel' | trans }} -

-
-
- {#{% if type == 'image' %}#} - - {% spaceless %} - - {% endspaceless %} -

- {{ 'previewLinkLabel' | trans }}    - {{ 'copyClipboardLabel' | trans }} -

-
- {#{% endif %}#} -
+
+

+ {{ 'share::share-record: advance' | trans }} + + {{ 'share::share-record: select-shared-def' | trans }} + +

+

+ + + {% trans %}Send to Twitter{% endtrans %} + +

+

+ + + {% trans %}Send to Facebook{% endtrans %} + +

+ +
+
+ + + +

+ {{ 'previewLinkLabel' | trans }}    + {{ 'copyClipboardLabel' | trans }} +

+
+
+ + +

+ {{ 'previewLinkLabel' | trans }}    + {{ 'copyClipboardLabel' | trans }} +

+
+
+ {#{% if type == 'image' %}#} + + {% spaceless %} + + {% endspaceless %} +

+ {{ 'previewLinkLabel' | trans }}    + {{ 'copyClipboardLabel' | trans }} +

+
+ {#{% endif %}#} +
+ + {% else %}
{{ 'No URL available' | trans }}
{% endif %} -{% endif %} +{% endif %} \ No newline at end of file diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ShareTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ShareTest.php index e8aa79d03d..f3163a99ef 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Prod/ShareTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Prod/ShareTest.php @@ -2,6 +2,7 @@ namespace Alchemy\Tests\Phrasea\Controller\Prod; +use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Controller\Prod\ShareController; use Alchemy\Phrasea\ControllerProvider\Prod\Share; use Symfony\Component\HttpKernel\Exception\HttpException; @@ -19,86 +20,63 @@ class ShareTest extends \PhraseanetAuthenticatedWebTestCase /** * @covers Alchemy\Phrasea\Controller\Prod\Share::shareRecord * @covers Alchemy\Phrasea\Controller\Prod\Share::connect - * @covers Alchemy\Phrasea\Controller\Prod\Share::call */ - public function testMountedRouteSlash() + public function testRouteSlashALL() { - $url = sprintf('/prod/share/record/%d/%d/', self::$DI['record_1']->get_base_id(), self::$DI['record_1']->get_record_id()); - self::$DI['client']->request('GET', $url); - $this->assertTrue(self::$DI['client']->getResponse()->isOk()); + $this->_RouteSlash("all", [0=>true, 1=>true, 2=>true, 3=>true]); } - /** - * @covers Alchemy\Phrasea\Controller\Prod\Share::shareRecord - * @covers Alchemy\Phrasea\Controller\Prod\Share::connect - */ - public function testRouteSlash() + public function testRouteSlashPublishers() { - $stubbedACL = $this->stubACL(); - - //has_right_on_base return true - $stubbedACL->expects($this->once()) - ->method('has_right_on_sbas') - ->will($this->returnValue(true)); - - //has_access_to_subdef return true - $stubbedACL->expects($this->once()) - ->method('has_access_to_subdef') - ->will($this->returnValue(true)); - - - $url = sprintf('/prod/share/record/%d/%d/', self::$DI['record_1']->get_base_id(), self::$DI['record_1']->get_record_id()); - self::$DI['client']->request('GET', $url); - $this->assertTrue(self::$DI['client']->getResponse()->isOk()); + $this->_RouteSlash("publishers", [0=>false, 1=>true, 2=>false, 3=>true]); + } + public function testRouteSlashNone() + { + $this->_RouteSlash("none", [0=>false, 1=>false, 2=>false, 3=>false]); + } + private function _RouteSlash($setting, $expected) + { + $app = $this->getApplication(); + $_conf = $app['conf']; + $app['conf'] = $this->getMockBuilder('Alchemy\Phrasea\Core\Configuration\PropertyAccess') + ->disableOriginalConstructor() + ->getMock(); + $app['conf'] + ->expects($this->any()) + ->method('get') + ->will($this->returnCallback(function ($param, $default) use ($_conf, $setting) { + switch ($param) { + case ['registry', 'actions', 'social-tools']: + return $setting; + } + return $_conf->get($param, $default); + })); + $result = []; + foreach($expected as $flags=>$v) { + $stubbedACL = $this->stubACL(); + // "has_right_on_sbas" IS checked by the route->before(), the url will return 403 + $stubbedACL->expects($this->any()) + ->method('has_right_on_sbas') + ->will($this->returnValue(($flags & 1) ? true:false)); + // but "has_access_to_subdef" IS NOT checked (the url will return a 200 with a message "no subdef to share") + $stubbedACL->expects($this->any()) + ->method('has_access_to_subdef') + ->will($this->returnValue(($flags & 2) ? true:false)); + $url = sprintf('/prod/share/record/%d/%d/', self::$DI['record_1']->get_base_id(), self::$DI['record_1']->get_record_id()); + self::$DI['client']->request('GET', $url); + $result[$flags] = self::$DI['client']->getResponse()->isOk(); + } + $this->assertEquals($expected, $result); } - /** * @covers Alchemy\Phrasea\Controller\Prod\Share::shareRecord */ public function testShareRecord() { $share = new ShareController(self::$DI['app']); - /** @var \record_adapter $record_1 */ $record_1 = self::$DI['record_1']; - $response = $share->shareRecord($record_1->getBaseId(), $record_1->getRecordId()); $this->assertTrue($response->isOk()); } - - /** - * @covers Alchemy\Phrasea\Controller\Prod\Share::shareRecord - */ - public function testShareRecordBadAccess() - { - $share = new ShareController(self::$DI['app']); - - $stubbedACL = $this->getMockBuilder('\ACL') - ->disableOriginalConstructor() - ->getMock(); - - //has_access_to_subdef return false - $stubbedACL->expects($this->once()) - ->method('has_access_to_subdef') - ->will($this->returnValue(false)); - - $aclProvider = $this->getMockBuilder('Alchemy\Phrasea\Authentication\ACLProvider') - ->disableOriginalConstructor() - ->getMock(); - $aclProvider->expects($this->any()) - ->method('get') - ->will($this->returnValue($stubbedACL)); - - self::$DI['app']['acl'] = $aclProvider; - - try { - $share->shareRecord(self::$DI['record_1']->get_base_id(), self::$DI['record_1']->get_record_id()); - } catch (HttpException $exception) { - $this->assertEquals(403, $exception->getStatusCode()); - - return; - } - - $this->fail('An access denied exception should have been thrown.'); - } }