From 55d7366fd7ddacab53175b57f8725b4bbd0d5f64 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Wed, 26 Feb 2014 13:39:41 +0100 Subject: [PATCH 1/7] Fix Xsendfile command --- .../Phrasea/Command/Setup/XSendFileMappingGenerator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php index 9610a42491..dcf3d69e04 100644 --- a/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php +++ b/lib/Alchemy/Phrasea/Command/Setup/XSendFileMappingGenerator.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Command\Setup; use Alchemy\Phrasea\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Alchemy\Phrasea\Http\XSendFile\XSendFileFactory; @@ -25,7 +26,7 @@ class XSendFileMappingGenerator extends Command $this->addOption('write', 'w', null, 'Writes the configuration') ->addOption('enabled', 'e', null, 'Set the enable toggle to `true`') - ->addArgument('type', null, 'The configuration type, either `nginx` or `apache`') + ->addArgument('type', InputArgument::REQUIRED, 'The configuration type, either `nginx` or `apache`') ->setDescription('Generates Phraseanet xsendfile mapping configuration depending on databoxes configuration'); } From 7f2be7d9aae1113ffe7813ac2d9eb9757a8e512f Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Wed, 26 Feb 2014 16:50:14 +0100 Subject: [PATCH 2/7] Use Guzzle\Http\Url for medias --- lib/Alchemy/Phrasea/Controller/Prod/Edit.php | 2 +- .../Phrasea/Controller/Prod/Records.php | 2 +- lib/classes/API/V1/adapter.php | 7 +++++-- lib/classes/Feed/XML/Abstract.php | 6 +++--- lib/classes/Feed/XML/Cooliris.php | 6 +++--- lib/classes/media/Permalink/Adapter.php | 7 ++++--- lib/classes/media/abstract.php | 8 +++++--- lib/classes/media/subdef.php | 19 +++++++++++-------- lib/classes/module/report/nav.php | 2 +- lib/classes/p4file.php | 4 +++- lib/classes/record/preview.php | 13 +++++++------ .../Tests/Phrasea/Application/ApiAbstract.php | 4 ++-- .../Phrasea/Controller/Root/RSSFeedTest.php | 2 +- .../Permalink/media_Permalink_AdapterTest.php | 4 ++-- tests/classes/media/media_subdefTest.php | 10 ++++++---- 15 files changed, 55 insertions(+), 41 deletions(-) diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php index 0177670ac9..25933800c0 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Edit.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Edit.php @@ -202,7 +202,7 @@ class Edit implements ControllerProviderInterface $thumbnail = $record->get_thumbnail(); $elements[$indice]['subdefs']['thumbnail'] = array( - 'url' => $thumbnail->get_url() + 'url' => (string) $thumbnail->get_url() , 'w' => $thumbnail->get_width() , 'h' => $thumbnail->get_height() ); diff --git a/lib/Alchemy/Phrasea/Controller/Prod/Records.php b/lib/Alchemy/Phrasea/Controller/Prod/Records.php index aae7f085eb..0fa37b5080 100644 --- a/lib/Alchemy/Phrasea/Controller/Prod/Records.php +++ b/lib/Alchemy/Phrasea/Controller/Prod/Records.php @@ -272,7 +272,7 @@ class Records implements ControllerProviderInterface $renewed = array(); foreach ($records as $record) { - $renewed[$record->get_serialize_key()] = $record->get_preview()->renew_url(); + $renewed[$record->get_serialize_key()] = (string) $record->get_preview()->renew_url(); }; return $app->json($renewed); diff --git a/lib/classes/API/V1/adapter.php b/lib/classes/API/V1/adapter.php index 7e6f0756c2..334ba53356 100644 --- a/lib/classes/API/V1/adapter.php +++ b/lib/classes/API/V1/adapter.php @@ -1728,6 +1728,9 @@ class API_V1_adapter extends API_V1_Abstract */ protected function list_permalink(media_Permalink_Adapter $permalink, registryInterface $registry) { + $downloadUrl = $permalink->get_url(); + $downloadUrl->getQuery()->set('download', '1'); + return array( 'created_on' => $permalink->get_created_on()->format(DATE_ATOM), 'id' => $permalink->get_id(), @@ -1735,8 +1738,8 @@ class API_V1_adapter extends API_V1_Abstract 'label' => $permalink->get_label(), 'updated_on' => $permalink->get_last_modified()->format(DATE_ATOM), 'page_url' => $permalink->get_page(), - 'download_url' => $permalink->get_url() . '&download', - 'url' => $permalink->get_url() + 'download_url' => (string) $downloadUrl, + 'url' => (string) $permalink->get_url() ); } diff --git a/lib/classes/Feed/XML/Abstract.php b/lib/classes/Feed/XML/Abstract.php index eab96e17f6..9895900274 100644 --- a/lib/classes/Feed/XML/Abstract.php +++ b/lib/classes/Feed/XML/Abstract.php @@ -294,7 +294,7 @@ abstract class Feed_XML_Abstract if ($preview_permalink) { $preview = $this->addTag($document, $group, 'media:content'); - $preview->setAttribute('url', $preview_permalink->get_url()); + $preview->setAttribute('url', (string) $preview_permalink->get_url()); $preview->setAttribute('fileSize', $preview_sd->get_size()); $preview->setAttribute('type', $preview_sd->get_mime()); $preview->setAttribute('medium', $medium); @@ -312,7 +312,7 @@ abstract class Feed_XML_Abstract if ($thumbnail_permalink) { $thumbnail = $this->addTag($document, $group, 'media:thumbnail'); - $thumbnail->setAttribute('url', $thumbnail_permalink->get_url()); + $thumbnail->setAttribute('url', (string) $thumbnail_permalink->get_url()); if ($thumbnail_sd->get_width()) $thumbnail->setAttribute('width', $thumbnail_sd->get_width()); @@ -321,7 +321,7 @@ abstract class Feed_XML_Abstract $thumbnail = $this->addTag($document, $group, 'media:content'); - $thumbnail->setAttribute('url', $thumbnail_permalink->get_url()); + $thumbnail->setAttribute('url', (string) $thumbnail_permalink->get_url()); $thumbnail->setAttribute('fileSize', $thumbnail_sd->get_size()); $thumbnail->setAttribute('type', $thumbnail_sd->get_mime()); $thumbnail->setAttribute('medium', $medium); diff --git a/lib/classes/Feed/XML/Cooliris.php b/lib/classes/Feed/XML/Cooliris.php index a5e32ae656..284a55f06d 100644 --- a/lib/classes/Feed/XML/Cooliris.php +++ b/lib/classes/Feed/XML/Cooliris.php @@ -394,7 +394,7 @@ class Feed_XML_Cooliris extends Feed_XML_Abstract implements Feed_XML_Interface if ($preview_permalink) { $preview = $this->addTag($document, $item, 'media:content'); - $preview->setAttribute('url', $preview_permalink->get_url()); + $preview->setAttribute('url', (string) $preview_permalink->get_url()); $preview->setAttribute('fileSize', $preview_sd->get_size()); $preview->setAttribute('type', $preview_sd->get_mime()); $preview->setAttribute('medium', $medium); @@ -412,7 +412,7 @@ class Feed_XML_Cooliris extends Feed_XML_Abstract implements Feed_XML_Interface if ($thumbnail_permalink) { $thumbnail = $this->addTag($document, $item, 'media:thumbnail'); - $thumbnail->setAttribute('url', $thumbnail_permalink->get_url()); + $thumbnail->setAttribute('url', (string) $thumbnail_permalink->get_url()); if ($thumbnail_sd->get_width()) $thumbnail->setAttribute('width', $thumbnail_sd->get_width()); @@ -421,7 +421,7 @@ class Feed_XML_Cooliris extends Feed_XML_Abstract implements Feed_XML_Interface $thumbnail = $this->addTag($document, $item, 'media:content'); - $thumbnail->setAttribute('url', $thumbnail_permalink->get_url()); + $thumbnail->setAttribute('url', (string) $thumbnail_permalink->get_url()); $thumbnail->setAttribute('fileSize', $thumbnail_sd->get_size()); $thumbnail->setAttribute('type', $thumbnail_sd->get_mime()); $thumbnail->setAttribute('medium', $medium); diff --git a/lib/classes/media/Permalink/Adapter.php b/lib/classes/media/Permalink/Adapter.php index 313b3dc740..cc5f3c444c 100644 --- a/lib/classes/media/Permalink/Adapter.php +++ b/lib/classes/media/Permalink/Adapter.php @@ -11,6 +11,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\Exception\RuntimeException; +use Guzzle\Http\Url; /** * @@ -142,19 +143,19 @@ class media_Permalink_Adapter implements media_Permalink_Interface, cache_cachea /** * - * @return string + * @return Url */ public function get_url() { $label = $this->get_label() . '.' . pathinfo($this->media_subdef->get_file(), PATHINFO_EXTENSION); - return $this->app->url('permalinks_permalink', array( + return Url::factory($this->app->url('permalinks_permalink', array( 'sbas_id' => $this->media_subdef->get_sbas_id(), 'record_id' => $this->media_subdef->get_record_id(), 'subdef' => $this->media_subdef->get_name(), 'label' => $label, 'token' => $this->get_token(), - )); + ))); } /** diff --git a/lib/classes/media/abstract.php b/lib/classes/media/abstract.php index 73f699a577..c9f4b32ca5 100644 --- a/lib/classes/media/abstract.php +++ b/lib/classes/media/abstract.php @@ -9,6 +9,8 @@ * file that was distributed with this source code. */ +use Guzzle\Http\Url; + /** * * @package subdefs @@ -19,7 +21,7 @@ abstract class media_abstract { /** * - * @var string + * @var Url */ protected $url; @@ -45,7 +47,7 @@ abstract class media_abstract * @param int $height * @return media */ - public function __construct($url, $width, $height) + public function __construct(Url $url, $width, $height) { $this->url = $url; $this->height = (int) $height; @@ -65,7 +67,7 @@ abstract class media_abstract /** * - * @return string + * @return Url */ public function get_url() { diff --git a/lib/classes/media/subdef.php b/lib/classes/media/subdef.php index 0849875302..9fdb5145fb 100644 --- a/lib/classes/media/subdef.php +++ b/lib/classes/media/subdef.php @@ -13,6 +13,7 @@ use Alchemy\Phrasea\Application; use MediaAlchemyst\Alchemyst; use MediaVorus\MediaVorus; use MediaVorus\Media\MediaInterface; +use Guzzle\Http\Url; /** * @@ -231,7 +232,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface , 'height' => $this->height , 'etag' => $this->etag , 'path' => $this->path - , 'url' => $this->url . ($this->is_physically_present ? '?etag=' . $this->etag : '') + , 'url' => $this->url , 'file' => $this->file , 'physically_present' => $this->is_physically_present , 'is_substituted' => $this->is_substituted @@ -280,7 +281,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $this->height = 256; $this->path = $this->app['root.path'] . '/www/skins/icons/substitution/'; $this->file = 'regroup_thumb.png'; - $this->url = '/skins/icons/substitution/regroup_thumb.png'; + $this->url = Url::factory('/skins/icons/substitution/regroup_thumb.png'); } else { $mime = $this->record->get_mime(); $mime = trim($mime) != '' ? str_replace('/', '_', $mime) : 'application_octet-stream'; @@ -290,7 +291,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface $this->height = 256; $this->path = $this->app['root.path'] . '/www/skins/icons/substitution/'; $this->file = str_replace('+', '%20', $mime) . '.png'; - $this->url = '/skins/icons/substitution/' . $this->file; + $this->url = Url::factory('/skins/icons/substitution/' . $this->file); } $this->is_physically_present = false; @@ -298,7 +299,7 @@ class media_subdef extends media_abstract implements cache_cacheableInterface if ( ! file_exists($this->path . $this->file)) { $this->path = $this->app['root.path'] . '/www/skins/icons/'; $this->file = 'substitution.png'; - $this->url = '/skins/icons/' . $this->file; + $this->url = Url::factory('/skins/icons/' . $this->file); } return $this; @@ -367,9 +368,11 @@ class media_subdef extends media_abstract implements cache_cacheableInterface public function get_url() { $url = parent::get_url(); - $etag = $this->getEtag(); + if (null !== $this->getEtag()) { + $url->getQuery()->set('etag', $this->getEtag()); + } - return $url . ($etag ? '?etag=' . $etag : ''); + return $url; } /** @@ -747,9 +750,9 @@ class media_subdef extends media_abstract implements cache_cacheableInterface } } - $this->url = "/datafiles/" . $this->record->get_sbas_id() + $this->url = Url::factory("/datafiles/" . $this->record->get_sbas_id() . "/" . $this->record->get_record_id() . "/" - . $this->get_name() . "/"; + . $this->get_name() . "/"); return; } diff --git a/lib/classes/module/report/nav.php b/lib/classes/module/report/nav.php index 1e499c8d45..194398ad5f 100644 --- a/lib/classes/module/report/nav.php +++ b/lib/classes/module/report/nav.php @@ -520,7 +520,7 @@ class module_report_nav extends module_report $this->result[] = array( 'photo' => "" + src='" . (string) $x->get_url() . "'>" , 'record_id' => $record->get_record_id() , 'date' => $this->app['date-formatter']->getPrettyString($document->get_creation_date()) , 'type' => $document->get_mime() diff --git a/lib/classes/p4file.php b/lib/classes/p4file.php index 0af334c3f0..d57234130b 100644 --- a/lib/classes/p4file.php +++ b/lib/classes/p4file.php @@ -9,6 +9,8 @@ * file that was distributed with this source code. */ +use Guzzle\Http\Url; + class p4file { @@ -52,7 +54,7 @@ class p4file } } - return $ret; + return Url::factory($ret); } } diff --git a/lib/classes/record/preview.php b/lib/classes/record/preview.php index b5c41c1351..4d98ab3257 100644 --- a/lib/classes/record/preview.php +++ b/lib/classes/record/preview.php @@ -11,6 +11,7 @@ use Alchemy\Phrasea\Application; use Alchemy\Phrasea\SearchEngine\SearchEngineInterface; +use Guzzle\Http\Url; /** * @@ -479,7 +480,7 @@ class record_preview extends record_adapter $width = 350; $height = 150; - $url = 'http://chart.apis.google.com/chart?' . + $url = Url::factory('http://chart.apis.google.com/chart?' . 'chs=' . $width . 'x' . $height . '&chd=t:' . implode(',', $views) . '&cht=lc' . @@ -492,7 +493,7 @@ class record_preview extends record_adapter . date_format(new DateTime(), 'd M') . '|1:|0|' . round($top / 2, 2) . '|' . $top . '|2:|min|average|max' . - '&chxp=2,' . $min . ',' . $average . ',' . $max; + '&chxp=2,' . $min . ',' . $average . ',' . $max); $this->view_popularity = new media_adapter($url, $width, $height); @@ -559,12 +560,12 @@ class record_preview extends record_adapter $width = 550; $height = 100; - $url = 'http://chart.apis.google.com/chart?' + $url = Url::factory('http://chart.apis.google.com/chart?' . 'cht=p3&chf=bg,s,00000000&chd=t:' . implode(',', $referrers) . '&chs=' . $width . 'x' . $height . '&chl=' - . urlencode(implode('|', array_keys($referrers))) . ''; + . urlencode(implode('|', array_keys($referrers)))); $this->refferer_popularity = new media_adapter($url, $width, $height); @@ -635,7 +636,7 @@ class record_preview extends record_adapter $width = 250; $height = 150; - $url = 'http://chart.apis.google.com/chart?' . + $url = Url::factory('http://chart.apis.google.com/chart?' . 'chs=' . $width . 'x' . $height . '&chd=t:' . implode(',', $dwnls) . '&cht=lc' . @@ -645,7 +646,7 @@ class record_preview extends record_adapter '&chxl=0:|' . date_format(new DateTime('-30 days'), 'd M') . '|' . date_format(new DateTime('-15 days'), 'd M') . '|' . date_format(new DateTime(), 'd M') . '|1:|0|' - . round($top / 2) . '|' . $top . ''; + . round($top / 2) . '|' . $top); $ret = new media_adapter($url, $width, $height); $this->download_popularity = $ret; diff --git a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php index 4a8b36f024..f3b179ba62 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php +++ b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php @@ -2226,7 +2226,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $expected = $subdef->get_permalink()->get_last_modified(); $found = \DateTime::createFromFormat(DATE_ATOM, $permalink['updated_on']); - + $this->assertLessThanOrEqual(1, $expected->diff($found)->format('U')); $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink['updated_on']); $this->assertDateAtom($permalink['updated_on']); @@ -2244,7 +2244,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertArrayHasKey("download_url", $permalink); $this->assertInternalType(\PHPUnit_Framework_Constraint_IsType::TYPE_STRING, $permalink['download_url']); - $this->assertEquals($subdef->get_permalink()->get_url() . '&download', $permalink['download_url']); + $this->assertEquals((string) $subdef->get_permalink()->get_url() . '&download=1', $permalink['download_url']); $this->checkUrlCode200($permalink['download_url']); $this->assertPermalinkHeaders($permalink['download_url'], $subdef, "download_url"); } diff --git a/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php b/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php index ddc12da773..6bfa68f6b8 100644 --- a/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php +++ b/tests/Alchemy/Tests/Phrasea/Controller/Root/RSSFeedTest.php @@ -561,7 +561,7 @@ class RssFeedTest extends \PhraseanetWebTestCaseAbstract foreach ($current_attributes as $attribute => $value) { switch ($attribute) { case "url": - $this->assertEquals($permalink->get_url(), $value); + $this->assertEquals((string) $permalink->get_url(), $value); break; case "fileSize": $this->assertEquals($ressource->get_size(), $value); diff --git a/tests/classes/media/Permalink/media_Permalink_AdapterTest.php b/tests/classes/media/Permalink/media_Permalink_AdapterTest.php index 3d15bb6cbf..d188ce8078 100644 --- a/tests/classes/media/Permalink/media_Permalink_AdapterTest.php +++ b/tests/classes/media/Permalink/media_Permalink_AdapterTest.php @@ -56,7 +56,7 @@ class media_Permalink_AdapterTest extends PhraseanetPHPUnitAbstract . '.' . pathinfo(self::$DI['record_1']->get_subdef('document')->get_file(), PATHINFO_EXTENSION) . '?token=' . static::$object->get_token(); - $this->assertEquals($url, static::$object->get_url()); + $this->assertEquals($url, (string) static::$object->get_url()); } public function testGet_Previewurl() @@ -72,7 +72,7 @@ class media_Permalink_AdapterTest extends PhraseanetPHPUnitAbstract . '.' . pathinfo(self::$DI['record_1']->get_subdef('preview')->get_file(), PATHINFO_EXTENSION) . '?token=' . $previewPermalink->get_token(); - $this->assertEquals($url, $previewPermalink->get_url()); + $this->assertEquals($url, (string) $previewPermalink->get_url()); } public function testGet_page() diff --git a/tests/classes/media/media_subdefTest.php b/tests/classes/media/media_subdefTest.php index 230d437d88..9ad3a7e4c2 100644 --- a/tests/classes/media/media_subdefTest.php +++ b/tests/classes/media/media_subdefTest.php @@ -91,8 +91,10 @@ class media_subdefTest extends \PhraseanetPHPUnitAbstract */ public function testGet_url() { - $this->assertEquals('/skins/icons/substitution/image_jpeg.png', self::$objectNotPresent->get_url()); - $this->assertRegExp('#\/datafiles\/' . self::$objectPresent->get_sbas_id() . '\/' . self::$objectPresent->get_record_id() . '\/preview\/\?etag=[0-9a-f]{32}#', self::$objectPresent->get_url()); + $this->assertInstanceOf('Guzzle\Http\Url', self::$objectNotPresent->get_url()); + $this->assertInstanceOf('Guzzle\Http\Url', self::$objectPresent->get_url()); + $this->assertEquals('/skins/icons/substitution/image_jpeg.png', (string) self::$objectNotPresent->get_url()); + $this->assertRegExp('#\/datafiles\/' . self::$objectPresent->get_sbas_id() . '\/' . self::$objectPresent->get_record_id() . '\/preview\/\?etag=[0-9a-f]{32}#', (string) self::$objectPresent->get_url()); } /** @@ -253,8 +255,8 @@ class media_subdefTest extends \PhraseanetPHPUnitAbstract */ public function testRenew_url() { - $this->assertInternalType('string', self::$objectPresent->renew_url()); - $this->assertInternalType('string', self::$objectNotPresent->renew_url()); + $this->assertInstanceOf('Guzzle\Http\Url', self::$objectPresent->renew_url()); + $this->assertInstanceOf('Guzzle\Http\Url', self::$objectNotPresent->renew_url()); } /** From c853f37c8960e6c552913ce92078f759c3bfc363 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Fri, 28 Feb 2014 17:48:37 +0100 Subject: [PATCH 3/7] Fix city setter on user_adapter --- lib/classes/User/Adapter.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/classes/User/Adapter.php b/lib/classes/User/Adapter.php index decfdd9cb0..04791fbb53 100644 --- a/lib/classes/User/Adapter.php +++ b/lib/classes/User/Adapter.php @@ -771,12 +771,13 @@ class User_Adapter implements User_Interface, cache_cacheableInterface public function set_geonameid($geonameid) { - $country_code = null; + $country_code = $city = null; try { - $country = $this->app['geonames.connector'] - ->geoname($this->geonameid) - ->get('country'); + $data = $this->app['geonames.connector'] + ->geoname($geonameid); + $country = $data->get('country'); + $city = $data->get('name'); if (isset($country['code'])) { $country_code = $country['code']; @@ -785,12 +786,13 @@ class User_Adapter implements User_Interface, cache_cacheableInterface } - $sql = 'UPDATE usr SET geonameid = :geonameid, pays=:country_code WHERE usr_id = :usr_id'; + $sql = 'UPDATE usr SET geonameid = :geonameid, pays=:country_code, ville = :city WHERE usr_id = :usr_id'; $datas = array( ':geonameid' => $geonameid, ':usr_id' => $this->get_id(), - ':country_code' => $country_code + ':country_code' => $country_code, + ':city' => $city, ); $stmt = $this->app['phraseanet.appbox']->get_connection()->prepare($sql); @@ -798,6 +800,7 @@ class User_Adapter implements User_Interface, cache_cacheableInterface $stmt->closeCursor(); $this->geonameid = $geonameid; $this->country = $country_code; + $this->city = $city; $this->delete_data_from_cache(); return $this; From 1952910c83a4597187dc3eb5f4042feaaeed62a2 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Fri, 28 Feb 2014 17:48:49 +0100 Subject: [PATCH 4/7] Add /me route on API v1 --- lib/Alchemy/Phrasea/Controller/Api/V1.php | 6 +++ lib/classes/API/V1/adapter.php | 47 +++++++++++++++++++ .../Tests/Phrasea/Application/ApiAbstract.php | 47 +++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/lib/Alchemy/Phrasea/Controller/Api/V1.php b/lib/Alchemy/Phrasea/Controller/Api/V1.php index 14ba57963e..8b14102a00 100644 --- a/lib/Alchemy/Phrasea/Controller/Api/V1.php +++ b/lib/Alchemy/Phrasea/Controller/Api/V1.php @@ -728,6 +728,12 @@ class V1 implements ControllerProviderInterface })->assert('databox_id', '\d+')->assert('story_id', '\d+'); $controllers->get('/stories/{any_id}/{anyother_id}/', $bad_request_exception); + $controllers->get('/me/', function (SilexApplication $app, Request $request) { + $result = $app['api']->get_current_user($app, $request); + + return $result->get_response(); + }); + return $controllers; } } diff --git a/lib/classes/API/V1/adapter.php b/lib/classes/API/V1/adapter.php index 7e6f0756c2..782ed7dd50 100644 --- a/lib/classes/API/V1/adapter.php +++ b/lib/classes/API/V1/adapter.php @@ -1949,6 +1949,53 @@ class API_V1_adapter extends API_V1_Abstract ); } + public function get_current_user(Application $app, Request $request) + { + $result = new API_V1_result($app, $request, $this); + $result->set_datas(array('user' => $this->list_user($app['authentication']->getUser()))); + + return $result; + } + + private function list_user(\User_Adapter $user) + { + switch ($user->get_gender()) { + case 2; + $gender = 'Mr'; + break; + case 1; + $gender = 'Mrs'; + break; + case 0; + $gender = 'Miss'; + break; + } + + return array( + 'id' => $user->get_id(), + 'email' => $user->get_email() ?: null, + 'login' => $user->get_login() ?: null, + 'first_name' => $user->get_firstname() ?: null, + 'last_name' => $user->get_lastname() ?: null, + 'display_name' => $user->get_display_name() ?: null, + 'gender' => $gender, + 'address' => $user->get_address() ?: null, + 'zip_code' => $user->get_zipcode() ?: null, + 'city' => $user->get_city() ?: null, + 'country' => $user->get_country() ?: null, + 'phone' => $user->get_tel() ?: null, + 'fax' => $user->get_fax() ?: null, + 'job' => $user->get_job() ?: null, + 'position' => $user->get_position() ?: null, + 'company' => $user->get_company() ?: null, + 'geoname_id' => $user->get_geonameid() ?: null, + 'last_connection' => $user->get_last_connection() ? $user->get_last_connection()->format(DATE_ATOM) : null, + 'created_on' => $user->get_creation_date() ? $user->get_creation_date()->format(DATE_ATOM) : null, + 'updated_on' => $user->get_modification_date() ? $user->get_modification_date()->format(DATE_ATOM) : null, + 'locale' => $user->get_locale() ?: null, + ); + } + /** * List all databoxes of the current appbox * diff --git a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php index 4a8b36f024..3f1d2321c4 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php +++ b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php @@ -2051,6 +2051,53 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertDateAtom($item['created_on']); } + public function testRouteMe() + { + $this->setToken(self::$token); + + $route = '/api/v1/me/'; + + $this->evaluateMethodNotAllowedRoute($route, array('POST', 'PUT', 'DELETE')); + + self::$DI['client']->request('GET', $route, $this->getParameters(), array(), array('HTTP_Accept' => $this->getAcceptMimeType())); + $content = $this->unserialize(self::$DI['client']->getResponse()->getContent()); + + $this->assertArrayHasKey('user', $content['response']); + + $this->evaluateGoodUserItem($content['response']['user'], self::$DI['user_notAdmin']); + } + + protected function evaluateGoodUserItem($data, \User_Adapter $user) + { + foreach (array( + 'id' => $user->get_id(), + 'email' => $user->get_email() ?: null, + 'login' => $user->get_login() ?: null, + 'first_name' => $user->get_firstname() ?: null, + 'last_name' => $user->get_lastname() ?: null, + 'display_name' => $user->get_display_name() ?: null, + 'address' => $user->get_address() ?: null, + 'zip_code' => $user->get_zipcode() ?: null, + 'city' => $user->get_city() ?: null, + 'country' => $user->get_country() ?: null, + 'phone' => $user->get_tel() ?: null, + 'fax' => $user->get_fax() ?: null, + 'job' => $user->get_job() ?: null, + 'position' => $user->get_position() ?: null, + 'company' => $user->get_company() ?: null, + 'geoname_id' => $user->get_geonameid() ?: null, + 'last_connection' => $user->get_last_connection() ? $user->get_last_connection()->format(DATE_ATOM) : null, + 'created_on' => $user->get_creation_date() ? $user->get_creation_date()->format(DATE_ATOM) : null, + 'updated_on' => $user->get_modification_date() ? $user->get_modification_date()->format(DATE_ATOM) : null, + 'locale' => $user->get_locale() ?: null, + ) as $key => $value) { + $this->assertArrayHasKey($key, $data); + if ($value) { + $this->assertEquals($value, $data[$key], 'Check key '.$key); + } + } + } + protected function evaluateGoodFeed($feed) { $this->assertArrayHasKey('id', $feed); From 28acde0882043c35d4b30a42d25ebddd3373b3a6 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Fri, 28 Feb 2014 19:03:33 +0100 Subject: [PATCH 5/7] Add user entities in API responses --- lib/classes/API/V1/Interface.php | 1 + lib/classes/API/V1/adapter.php | 23 +++++++---- .../Tests/Phrasea/Application/ApiAbstract.php | 39 +++++++++++++------ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/lib/classes/API/V1/Interface.php b/lib/classes/API/V1/Interface.php index f0fb2ac7ab..fe0b3ba68d 100644 --- a/lib/classes/API/V1/Interface.php +++ b/lib/classes/API/V1/Interface.php @@ -9,6 +9,7 @@ * file that was distributed with this source code. */ +use Alchemy\Phrasea\Application; use Symfony\Component\HttpFoundation\Request; /** diff --git a/lib/classes/API/V1/adapter.php b/lib/classes/API/V1/adapter.php index 782ed7dd50..6d0814451b 100644 --- a/lib/classes/API/V1/adapter.php +++ b/lib/classes/API/V1/adapter.php @@ -39,6 +39,7 @@ class API_V1_adapter extends API_V1_Abstract */ protected $app; + const OBJECT_TYPE_USER = 'http://api.phraseanet.com/api/objects/user'; const OBJECT_TYPE_STORY = 'http://api.phraseanet.com/api/objects/story'; const OBJECT_TYPE_STORY_METADATA_BAG = 'http://api.phraseanet.com/api/objects/story-metadata-bag'; @@ -803,14 +804,16 @@ class API_V1_adapter extends API_V1_Abstract } } - $usr_id = null; + $usr_id = $user = null; if ($file->getSession()->getUser($this->app)) { - $usr_id = $file->getSession()->getUser($this->app)->get_id(); + $user = $file->getSession()->getUser($this->app); + $usr_id = $user->get_id(); } $session = array( 'id' => $file->getSession()->getId(), 'usr_id' => $usr_id, + 'user' => $user ? $this->list_user($user) : null, ); return array( @@ -1390,6 +1393,7 @@ class API_V1_adapter extends API_V1_Abstract 'can_agree' => $participant->getCanAgree(), 'can_see_others' => $participant->getCanSeeOthers(), 'readonly' => $user->get_id() != $this->app['authentication']->getUser()->get_id(), + 'user' => $this->list_user($user), ), 'agreement' => $validation_datas->getAgreement(), 'updated_on' => $validation_datas->getUpdated()->format(DATE_ATOM), @@ -1808,10 +1812,12 @@ class API_V1_adapter extends API_V1_Abstract { $ret = array( 'basket_id' => $basket->getId(), + 'owner' => $this->list_user($basket->getOwner($this->app)), 'created_on' => $basket->getCreated()->format(DATE_ATOM), 'description' => (string) $basket->getDescription(), 'name' => $basket->getName(), 'pusher_usr_id' => $basket->getPusherId(), + 'pusher' => $basket->getPusher($this->app) ? $this->list_user($basket->getPusher($this->app)) : null, 'updated_on' => $basket->getUpdated()->format(DATE_ATOM), 'unread' => !$basket->getIsRead(), 'validation_basket' => !!$basket->getValidation() @@ -1831,6 +1837,7 @@ class API_V1_adapter extends API_V1_Abstract 'can_agree' => $participant->getCanAgree(), 'can_see_others' => $participant->getCanSeeOthers(), 'readonly' => $user->get_id() != $this->app['authentication']->getUser()->get_id(), + 'user' => $this->list_user($user), ); } @@ -1842,11 +1849,12 @@ class API_V1_adapter extends API_V1_Abstract $ret = array_merge( array( - 'validation_users' => $users, - 'expires_on' => $expires_on_atom, - 'validation_infos' => $basket->getValidation()->getValidationString($this->app, $this->app['authentication']->getUser()), - 'validation_confirmed' => $basket->getValidation()->getParticipant($this->app['authentication']->getUser(), $this->app)->getIsConfirmed(), - 'validation_initiator' => $basket->getValidation()->isInitiator($this->app['authentication']->getUser()), + 'validation_users' => $users, + 'expires_on' => $expires_on_atom, + 'validation_infos' => $basket->getValidation()->getValidationString($this->app, $this->app['authentication']->getUser()), + 'validation_confirmed' => $basket->getValidation()->getParticipant($this->app['authentication']->getUser(), $this->app)->getIsConfirmed(), + 'validation_initiator' => $basket->getValidation()->isInitiator($this->app['authentication']->getUser()), + 'validation_initiator_user' => $this->list_user($basket->getValidation()->getInitiator($this->app)), ), $ret ); } @@ -1972,6 +1980,7 @@ class API_V1_adapter extends API_V1_Abstract } return array( + '@entity@' => self::OBJECT_TYPE_USER, 'id' => $user->get_id(), 'email' => $user->get_email() ?: null, 'login' => $user->get_login() ?: null, diff --git a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php index 3f1d2321c4..b3198155aa 100644 --- a/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php +++ b/tests/Alchemy/Tests/Phrasea/Application/ApiAbstract.php @@ -13,7 +13,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract { /** * - * @var Symfony\Component\HttpKernel\Client + * @var \Symfony\Component\HttpKernel\Client */ protected $client; @@ -1160,7 +1160,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertArrayHasKey("baskets", $content['response']); foreach ($content['response']['baskets'] as $basket) { - $this->evaluateGoodBasket($basket); + $this->evaluateGoodBasket($basket, self::$DI['user_notAdmin']); } $route = '/api/v1/records/24892534/51654651553/related/'; @@ -1351,7 +1351,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertArrayHasKey("baskets", $content['response']); foreach ($content['response']['baskets'] as $basket) { - $this->evaluateGoodBasket($basket); + $this->evaluateGoodBasket($basket, self::$DI['user']); } } @@ -1375,7 +1375,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertEquals(1, count($content['response'])); $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user_notAdmin']); $this->assertEquals('un Joli Nom', $content['response']['basket']['name']); } @@ -1405,7 +1405,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertArrayHasKey("basket_elements", $content['response']); $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user']); foreach ($content['response']['basket_elements'] as $basket_element) { $this->assertArrayHasKey('basket_element_id', $basket_element); @@ -1442,7 +1442,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertEquals(1, count((array) $content['response'])); $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user']); $this->assertEquals($content['response']['basket']['name'], 'un Joli Nom'); @@ -1456,7 +1456,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user']); $this->assertEquals($content['response']['basket']['name'], 'un Joli Nom'); @@ -1468,7 +1468,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertEquals(1, count((array) $content['response'])); $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user']); $this->assertEquals($content['response']['basket']['name'], 'aƩaa'); } @@ -1496,7 +1496,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertEquals(1, count((array) $content['response'])); $this->assertArrayHasKey("basket", $content['response']); - $this->evaluateGoodBasket($content['response']['basket']); + $this->evaluateGoodBasket($content['response']['basket'], self::$DI['user']); $this->assertEquals($content['response']['basket']['description'], 'une belle desc'); } @@ -1523,7 +1523,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $found = false; foreach ($content['response']['baskets'] as $basket) { - $this->evaluateGoodBasket($basket); + $this->evaluateGoodBasket($basket, self::$DI['user']); $found = true; } if (!$found) { @@ -2037,6 +2037,15 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract { $this->assertArrayHasKey('id', $item); $this->assertArrayHasKey('quarantine_session', $item); + + $session = $item['quarantine_session']; + $this->assertArrayHasKey('id', $session); + $this->assertArrayHasKey('usr_id', $session); + $this->assertArrayHasKey('user', $session); + if ($session['user'] !== null) { + $this->evaluateGoodUserItem($session['user'], self::$DI['user_notAdmin']); + } + $this->assertArrayHasKey('base_id', $item); $this->assertArrayHasKey('original_name', $item); $this->assertArrayHasKey('sha256', $item); @@ -2070,6 +2079,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract protected function evaluateGoodUserItem($data, \User_Adapter $user) { foreach (array( + '@entity@' => \API_V1_adapter::OBJECT_TYPE_USER, 'id' => $user->get_id(), 'email' => $user->get_email() ?: null, 'login' => $user->get_login() ?: null, @@ -2091,7 +2101,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract 'updated_on' => $user->get_modification_date() ? $user->get_modification_date()->format(DATE_ATOM) : null, 'locale' => $user->get_locale() ?: null, ) as $key => $value) { - $this->assertArrayHasKey($key, $data); + $this->assertArrayHasKey($key, $data, 'Assert key is present '.$key); if ($value) { $this->assertEquals($value, $data[$key], 'Check key '.$key); } @@ -2434,9 +2444,13 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract $this->assertEquals(405, $response->getStatusCode(), 'Test status code 405 ' . $response->getContent()); } - protected function evaluateGoodBasket($basket) + protected function evaluateGoodBasket($basket, \User_Adapter $user) { $this->assertTrue(is_array($basket)); + $this->assertArrayHasKey('basket_id', $basket); + $this->assertArrayHasKey('owner', $basket); + $this->evaluateGoodUserItem($basket['owner'], $user); + $this->assertArrayHasKey('pusher', $basket); $this->assertArrayHasKey('created_on', $basket); $this->assertArrayHasKey('description', $basket); $this->assertArrayHasKey('name', $basket); @@ -2446,6 +2460,7 @@ abstract class ApiAbstract extends \PhraseanetWebTestCaseAbstract if (!is_null($basket['pusher_usr_id'])) { $this->assertTrue(is_int($basket['pusher_usr_id'])); + $this->evaluateGoodUserItem($basket['pusher'], self::$DI['user_notAdmin']); } $this->assertTrue(is_string($basket['name'])); From a3b941c3ec92a7e605e464e530d2168994545dde Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 27 Feb 2014 14:14:07 +0100 Subject: [PATCH 6/7] Add H264 pseudo streaming tools --- bin/console | 4 + config/configuration.sample.yml | 4 + .../Command/Setup/H264ConfigurationDumper.php | 56 +++++++++ .../Command/Setup/H264MappingGenerator.php | 111 +++++++++++++++++ .../Provider/FileServeServiceProvider.php | 9 ++ ...endFileMode.php => AbstractServerMode.php} | 4 +- .../Http/H264PseudoStreaming/Apache.php | 109 +++++++++++++++++ .../Http/H264PseudoStreaming/H264Factory.php | 97 +++++++++++++++ .../H264PseudoStreaming/H264Interface.php | 25 ++++ .../Http/H264PseudoStreaming/Nginx.php | 114 ++++++++++++++++++ .../Http/H264PseudoStreaming/NullMode.php | 30 +++++ .../Phrasea/Http/ServeFileResponseFactory.php | 1 - .../Phrasea/Http/ServerModeInterface.php | 22 ++++ .../Phrasea/Http/XSendFile/ApacheMode.php | 3 +- .../Phrasea/Http/XSendFile/ModeInterface.php | 10 +- .../Phrasea/Http/XSendFile/NginxMode.php | 3 +- .../Http/XSendFile/XSendFileFactory.php | 4 +- lib/classes/API/V1/adapter.php | 6 +- lib/classes/media/subdef.php | 5 +- lib/classes/p4file.php | 60 --------- lib/classes/patch/384alpha1a.php | 63 ++++++++++ lib/conf.d/_GV_template.inc | 28 ----- lib/conf.d/configuration.yml | 4 + templates/web/common/thumbnail.html.twig | 18 ++- .../Provider/FileServeServiceProviderTest.php | 10 ++ .../Http/H264PseudoStreaming/ApacheTest.php | 46 +++++++ .../H264PseudoStreaming/H264FactoryTest.php | 84 +++++++++++++ .../Http/H264PseudoStreaming/NginxTest.php | 46 +++++++ 28 files changed, 864 insertions(+), 112 deletions(-) create mode 100644 lib/Alchemy/Phrasea/Command/Setup/H264ConfigurationDumper.php create mode 100644 lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php rename lib/Alchemy/Phrasea/Http/{XSendFile/AbstractXSendFileMode.php => AbstractServerMode.php} (94%) create mode 100644 lib/Alchemy/Phrasea/Http/H264PseudoStreaming/Apache.php create mode 100644 lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Factory.php create mode 100644 lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Interface.php create mode 100644 lib/Alchemy/Phrasea/Http/H264PseudoStreaming/Nginx.php create mode 100644 lib/Alchemy/Phrasea/Http/H264PseudoStreaming/NullMode.php create mode 100644 lib/Alchemy/Phrasea/Http/ServerModeInterface.php delete mode 100644 lib/classes/p4file.php create mode 100644 lib/classes/patch/384alpha1a.php create mode 100644 tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/ApacheTest.php create mode 100644 tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/H264FactoryTest.php create mode 100644 tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/NginxTest.php diff --git a/bin/console b/bin/console index 3c603f819c..449afd50d7 100755 --- a/bin/console +++ b/bin/console @@ -17,6 +17,8 @@ namespace KonsoleKommander; * @link www.phraseanet.com */ use Alchemy\Phrasea\Command\Plugin\ListPlugin; +use Alchemy\Phrasea\Command\Setup\H264ConfigurationDumper; +use Alchemy\Phrasea\Command\Setup\H264MappingGenerator; use Alchemy\Phrasea\Core\Version; use Alchemy\Phrasea\Command\BuildMissingSubdefs; use Alchemy\Phrasea\Command\CreateCollection; @@ -98,6 +100,8 @@ $cli->command(new AddPlugin()); $cli->command(new ListPlugin()); $cli->command(new RemovePlugin()); $cli->command(new Configuration()); +$cli->command(new H264ConfigurationDumper()); +$cli->command(new H264MappingGenerator()); $cli->command(new XSendFileConfigurationDumper()); $cli->command(new XSendFileMappingGenerator()); diff --git a/config/configuration.sample.yml b/config/configuration.sample.yml index daa3746f08..153482aea3 100644 --- a/config/configuration.sample.yml +++ b/config/configuration.sample.yml @@ -137,4 +137,8 @@ xsendfile: enabled: false type: nginx mapping: [] +h264-pseudo-streaming: + enabled: false + type: nginx + mapping: [] plugins: [] diff --git a/lib/Alchemy/Phrasea/Command/Setup/H264ConfigurationDumper.php b/lib/Alchemy/Phrasea/Command/Setup/H264ConfigurationDumper.php new file mode 100644 index 0000000000..103efb04f4 --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Setup/H264ConfigurationDumper.php @@ -0,0 +1,56 @@ +setDescription('Dump the virtual host configuration depending on Phraseanet configuration'); + } + + /** + * {@inheritdoc} + */ + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $output->writeln(''); + + if (!$this->container['phraseanet.h264-factory']->isH264Enabled()) { + $output->writeln('H264 pseudo streaming support is disabled'); + $ret = 1; + } else { + $output->writeln('H264 pseudo streaming support is enabled'); + $ret = 0; + } + + try { + $configuration = $this->container['phraseanet.h264-factory']->createMode(true, true)->getVirtualHostConfiguration(); + $output->writeln('H264 pseudo streaming configuration seems OK'); + $output->writeln($configuration); + } catch (RuntimeException $e) { + $output->writeln('H264 pseudo streaming configuration seems invalid'); + $ret = 1; + } + + $output->writeln(''); + + return $ret; + } +} diff --git a/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php b/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php new file mode 100644 index 0000000000..a897bfa88a --- /dev/null +++ b/lib/Alchemy/Phrasea/Command/Setup/H264MappingGenerator.php @@ -0,0 +1,111 @@ +addOption('write', 'w', null, 'Writes the configuration') + ->addOption('enabled', 'e', null, 'Set the enable toggle to `true`') + ->addArgument('type', InputArgument::REQUIRED, 'The configuration type, either `nginx` or `apache`') + ->setDescription('Generates Phraseanet H264 pseudo streaming mapping configuration depending on databoxes configuration'); + } + + /** + * {@inheritdoc} + */ + protected function doExecute(InputInterface $input, OutputInterface $output) + { + $paths = $this->extractPath($this->container['phraseanet.appbox']); + foreach ($paths as $path) { + $this->container['filesystem']->mkdir($path); + } + + $type = strtolower($input->getArgument('type')); + $enabled = $input->getOption('enabled'); + + $factory = new H264Factory($this->container['monolog'], true, $type, $this->computeMapping($paths)); + $mode = $factory->createMode(true); + + $currentConf = isset($this->container['phraseanet.configuration']['h264-pseudo-streaming']) ? $this->container['phraseanet.configuration']['h264-pseudo-streaming'] : array(); + $currentMapping = (isset($currentConf['mapping']) && is_array($currentConf['mapping'])) ? $currentConf['mapping'] : array(); + + $conf = array( + 'enabled' => $enabled, + 'type' => $type, + 'mapping' => array_replace_recursive($mode->getMapping(), $currentMapping), + ); + + if ($input->getOption('write')) { + $output->write("Writing configuration ..."); + $this->container['phraseanet.configuration']['h264-pseudo-streaming'] = $conf; + $output->writeln(" OK"); + $output->writeln(""); + $output->write("It is now strongly recommended to use h264-pseudo-streaming:dump-configuration command to upgrade your virtual-host"); + } else { + $output->writeln("Configuration will not be written, use --write option to write it"); + $output->writeln(""); + $output->writeln(Yaml::dump(array('h264-pseudo-streaming' => $conf), 4)); + } + + return 0; + } + + private function computeMapping($paths) + { + $paths = array_unique($paths); + + $ret = array(); + + foreach ($paths as $path) { + $ret[$path] = $this->pathsToConf($path); + } + + return $ret; + } + + private function pathsToConf($path) + { + static $n = 0; + $n++; + + return array('mount-point' => 'mp4-videos-'.$n, 'directory' => $path, 'passphrase' => \random::generatePassword(32)); + } + + private function extractPath(\appbox $appbox) + { + $paths = array(); + + foreach ($appbox->get_databoxes() as $databox) { + foreach ($databox->get_subdef_structure() as $group => $subdefs) { + if ('video' !== $group) { + continue; + } + foreach ($subdefs as $subdef) { + $paths[] = $subdef->get_path(); + } + } + } + + return array_filter(array_unique($paths)); + } +} diff --git a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php index 0697d1bba1..a22ab5d29d 100644 --- a/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php +++ b/lib/Alchemy/Phrasea/Core/Provider/FileServeServiceProvider.php @@ -12,6 +12,7 @@ namespace Alchemy\Phrasea\Core\Provider; use Alchemy\Phrasea\Core\Event\Subscriber\XSendFileSubscriber; +use Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory; use Alchemy\Phrasea\Http\ServeFileResponseFactory; use Alchemy\Phrasea\Http\XSendFile\XSendFileFactory; use Silex\Application; @@ -28,6 +29,14 @@ class FileServeServiceProvider implements ServiceProviderInterface return XSendFileFactory::create($app); }); + $app['phraseanet.h264-factory'] = $app->share(function ($app) { + return H264Factory::create($app); + }); + + $app['phraseanet.h264'] = $app->share(function ($app) { + return $app['phraseanet.h264-factory']->createMode(false); + }); + $app['phraseanet.file-serve'] = $app->share(function (Application $app) { return ServeFileResponseFactory::create($app); }); diff --git a/lib/Alchemy/Phrasea/Http/XSendFile/AbstractXSendFileMode.php b/lib/Alchemy/Phrasea/Http/AbstractServerMode.php similarity index 94% rename from lib/Alchemy/Phrasea/Http/XSendFile/AbstractXSendFileMode.php rename to lib/Alchemy/Phrasea/Http/AbstractServerMode.php index 29439e1a00..71fde43df8 100644 --- a/lib/Alchemy/Phrasea/Http/XSendFile/AbstractXSendFileMode.php +++ b/lib/Alchemy/Phrasea/Http/AbstractServerMode.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -namespace Alchemy\Phrasea\Http\XSendFile; +namespace Alchemy\Phrasea\Http; use Alchemy\Phrasea\Exception\InvalidArgumentException; -abstract class AbstractXSendFileMode +abstract class AbstractServerMode { protected $mapping = array(); diff --git a/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/Apache.php b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/Apache.php new file mode 100644 index 0000000000..4863d7e3da --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/Apache.php @@ -0,0 +1,109 @@ + $entry) { + if (!is_array($entry)) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must be an array'); + } + + if (!isset($entry['directory'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "directory" key'); + } + + if (!isset($entry['mount-point'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "mount-point" key'); + } + + if (!isset($entry['passphrase'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "passphrase" key'); + } + + if (false === is_dir(trim($entry['directory'])) || '' === trim($entry['mount-point']) || '' === trim($entry['passphrase'])) { + continue; + } + + $final[$key] = array( + 'directory' => $this->sanitizePath(realpath($entry['directory'])), + 'mount-point' => $this->sanitizeMountPoint($entry['mount-point']), + 'passphrase' => trim($entry['passphrase']), + ); + } + + $this->mapping = $final; + } + + /** + * {@inheritdoc} + */ + public function getUrl($pathfile) + { + if (!is_file($pathfile)) { + return null; + } + $pathfile = realpath($pathfile); + + foreach ($this->mapping as $entry) { + if (0 !== strpos($pathfile, $entry['directory'])) { + continue; + } + + return $this->generateUrl($pathfile, $entry); + } + } + + /** + * {@inheritdoc} + */ + public function getVirtualHostConfiguration() + { + $output = "\n"; + foreach ($this->mapping as $entry) { + $output .= " Alias ".$entry['mount-point']." \"".$entry['directory']."\"\n"; + $output .= "\n"; + $output .= " \n"; + $output .= " AuthTokenSecret \"".$entry['passphrase']."\"\n"; + $output .= " AuthTokenPrefix ".$entry['mount-point']."\n"; + $output .= " AuthTokenTimeout 3600\n"; + $output .= " AuthTokenLimitByIp off\n"; + $output .= " \n"; + $output .= "\n"; + } + + return $output; + } + + private function generateUrl($pathfile, array $entry) + { + $path = substr($pathfile, strlen($entry['directory'])); + + $hexTime = dechex(time() + 3600); + $token = md5($entry['passphrase'] . $path . $hexTime); + + return Url::factory($entry['mount-point'] .'/'. $token . "/" . $hexTime . $path); + } +} diff --git a/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Factory.php b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Factory.php new file mode 100644 index 0000000000..87db6205b0 --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Factory.php @@ -0,0 +1,97 @@ +logger = $logger; + $this->enabled = (Boolean) $enabled; + $this->type = strtolower($type); + $this->mapping = $mapping; + } + + /** + * Returns a new instance of H264Interface. + * + * @return H264Interface + * + * @throws InvalidArgumentException if mode type is unknown + */ + public function createMode($throwException = false, $forceMode = false) + { + if (false === $this->enabled && true !== $forceMode) { + return new NullMode(); + } + + switch ($this->type) { + case 'apache': + case 'apache2': + return new Apache($this->mapping); + case 'nginx': + return new Nginx($this->mapping); + default: + $this->logger->error('Invalid h264 pseudo streaming configuration.'); + if ($throwException) { + throw new InvalidArgumentException(sprintf('Invalid h264 pseudo streaming configuration width type "%s"', $this->type)); + } + + return new NullMode(); + } + } + + /** + * Creates a new instance of H264 Factory given a configuration. + * + * @param Application $app + * + * @return H264Factory + */ + public static function create(Application $app) + { + $conf = $app['phraseanet.configuration']['h264-pseudo-streaming']; + + $mapping = array(); + + if (isset($conf['mapping'])) { + $mapping = $conf['mapping']; + } + + return new self($app['monolog'], $conf['enabled'], $conf['type'], $mapping); + } + + /** + * @return Boolean + */ + public function isH264Enabled() + { + return $this->enabled; + } +} diff --git a/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Interface.php b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Interface.php new file mode 100644 index 0000000000..a9f9ab793f --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/H264Interface.php @@ -0,0 +1,25 @@ + $entry) { + if (!is_array($entry)) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must be an array'); + } + + if (!isset($entry['directory'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "directory" key'); + } + + if (!isset($entry['mount-point'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "mount-point" key'); + } + + if (!isset($entry['passphrase'])) { + throw new InvalidArgumentException('H264 pseudo streaming mapping entry must contain at least a "passphrase" key'); + } + + if (false === is_dir(trim($entry['directory'])) || '' === trim($entry['mount-point']) || '' === trim($entry['passphrase'])) { + continue; + } + + $final[$key] = array( + 'directory' => $this->sanitizePath(realpath($entry['directory'])), + 'mount-point' => $this->sanitizeMountPoint($entry['mount-point']), + 'passphrase' => trim($entry['passphrase']), + ); + } + + $this->mapping = $final; + } + + /** + * {@inheritdoc} + */ + public function getUrl($pathfile) + { + if (!is_file($pathfile)) { + return null; + } + $pathfile = realpath($pathfile); + + foreach ($this->mapping as $entry) { + if (0 !== strpos($pathfile, $entry['directory'])) { + continue; + } + + return $this->generateUrl($pathfile, $entry); + } + } + + /** + * {@inheritdoc} + */ + public function getVirtualHostConfiguration() + { + $output = "\n"; + foreach ($this->mapping as $entry) { + $output .= " location " . $entry['mount-point']. " {\n"; + $output .= " mp4;\n"; + $output .= " secure_link \$arg_hash,\$arg_expires;\n"; + $output .= " secure_link_md5 \"\$secure_link_expires\$uri ".$entry['passphrase']."\";\n"; + $output .= " \n"; + $output .= " if (\$secure_link = \"\") {\n"; + $output .= " return 403;\n"; + $output .= " }\n"; + $output .= " if (\$secure_link = \"0\") {\n"; + $output .= " return 410;\n"; + $output .= " }\n"; + $output .= " \n"; + $output .= " alias ".$entry['directory'].";\n"; + $output .= " }\n"; + } + + return $output; + } + + private function generateUrl($pathfile, array $entry) + { + $path = $entry['mount-point'].substr($pathfile, strlen($entry['directory'])); + $expire = time() + 3600; // At which point in time the file should expire. time() + x; would be the usual usage. + + $hash = str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode(md5($expire.$path.' '.$entry['passphrase'], true))); + + return Url::factory($path.'?hash='.$hash.'&expires='.$expire); + } +} diff --git a/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/NullMode.php b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/NullMode.php new file mode 100644 index 0000000000..32addaa8dd --- /dev/null +++ b/lib/Alchemy/Phrasea/Http/H264PseudoStreaming/NullMode.php @@ -0,0 +1,30 @@ + array( 'XsendFile' => $app['phraseanet.configuration']['xsendfile']['enabled'], 'XsendFileMapping' => $app['phraseanet.configuration']['xsendfile']['mapping'], - 'h264Streaming' => $app['phraseanet.registry']->get('GV_h264_streaming'), - 'authTokenDirectory' => $app['phraseanet.registry']->get('GV_mod_auth_token_directory'), - 'authTokenDirectoryPath' => $app['phraseanet.registry']->get('GV_mod_auth_token_directory_path'), - 'authTokenPassphrase' => $app['phraseanet.registry']->get('GV_mod_auth_token_passphrase'), + 'H264PseudoStreaming' => $app['phraseanet.configuration']['h264-pseudo-streaming']['enabled'], + 'H264PseudoStreamingMapping' => $app['phraseanet.configuration']['h264-pseudo-streaming']['mapping'], ) ), 'maintenance' => array( diff --git a/lib/classes/media/subdef.php b/lib/classes/media/subdef.php index 9fdb5145fb..9ad0ac00f8 100644 --- a/lib/classes/media/subdef.php +++ b/lib/classes/media/subdef.php @@ -742,9 +742,8 @@ class media_subdef extends media_abstract implements cache_cacheableInterface } if (in_array($this->mime, array('video/mp4'))) { - $token = p4file::apache_tokenize($this->app['phraseanet.registry'], $this->get_pathfile()); - if ($token) { - $this->url = $token; + if (null !== $url = $this->app['phraseanet.h264']->getUrl($this->get_pathfile())) { + $this->url = $url; return; } diff --git a/lib/classes/p4file.php b/lib/classes/p4file.php deleted file mode 100644 index d57234130b..0000000000 --- a/lib/classes/p4file.php +++ /dev/null @@ -1,60 +0,0 @@ -get('GV_h264_streaming') && is_file($file)) { - if (($pos = mb_strpos($file, $registry->get('GV_mod_auth_token_directory_path'))) === false) { - return false; - } - - $server = new system_server(); - - if ($server->is_nginx()) { - $fileToProtect = mb_substr($file, mb_strlen($registry->get('GV_mod_auth_token_directory_path'))); - - $secret = $registry->get('GV_mod_auth_token_passphrase'); - $protectedPath = p4string::addFirstSlash(p4string::delEndSlash($registry->get('GV_mod_auth_token_directory'))); - - $hexTime = strtoupper(dechex(time() + 3600)); - - $token = md5($protectedPath . $fileToProtect . '/' . $secret . '/' . $hexTime); - - $url = $protectedPath . $fileToProtect . '/' . $token . '/' . $hexTime; - - $ret = $url; - } elseif ($server->is_apache()) { - $fileToProtect = mb_substr($file, mb_strlen($registry->get('GV_mod_auth_token_directory_path'))); - - $secret = $registry->get('GV_mod_auth_token_passphrase'); // Same as AuthTokenSecret - $protectedPath = p4string::addEndSlash(p4string::delFirstSlash($registry->get('GV_mod_auth_token_directory'))); // Same as AuthTokenPrefix - $hexTime = dechex(time()); // Time in Hexadecimal - - $token = md5($secret . $fileToProtect . $hexTime); - - // We build the url - $url = '/' . $protectedPath . $token . "/" . $hexTime . $fileToProtect; - - $ret = $url; - } - } - - return Url::factory($ret); - } - -} diff --git a/lib/classes/patch/384alpha1a.php b/lib/classes/patch/384alpha1a.php new file mode 100644 index 0000000000..9c3a53975f --- /dev/null +++ b/lib/classes/patch/384alpha1a.php @@ -0,0 +1,63 @@ +release; + } + + /** + * {@inheritdoc} + */ + public function require_all_upgrades() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function concern() + { + return $this->concern; + } + + /** + * {@inheritdoc} + */ + public function apply(base $appbox, Application $app) + { + $config = $app['phraseanet.configuration']->getConfig(); + + $config['h264-pseudo-streaming'] = array( + 'enabled' => false, + 'type' => null, + 'mapping' => array(), + ); + + $app['phraseanet.configuration']->setConfig($config); + + return true; + } +} diff --git a/lib/conf.d/_GV_template.inc b/lib/conf.d/_GV_template.inc index 62c401375a..ea73e1803e 100644 --- a/lib/conf.d/_GV_template.inc +++ b/lib/conf.d/_GV_template.inc @@ -229,34 +229,6 @@ return call_user_func_array(function(Application $app) { ), array( 'section' => _('Executables settings'), 'vars' => array( - array( - 'type' => \registry::TYPE_BOOLEAN, - 'name' => 'GV_h264_streaming', - 'comment' => _('Enable H264 stream mode'), - 'help' => _('Use with mod_token. Attention requires the apache modules and mod_h264_streaming mod_auth_token'), - 'default' => false - ), - array( - 'type' => \registry::TYPE_STRING, - 'name' => 'GV_mod_auth_token_directory', - 'end_slash' => true, - 'comment' => _('Auth_token mount point'), - 'default' => false - ), - array( - 'type' => \registry::TYPE_STRING, - 'name' => 'GV_mod_auth_token_directory_path', - 'end_slash' => false, - 'comment' => _('Auth_token directory path'), - 'default' => false - ), - array( - 'type' => \registry::TYPE_STRING, - 'name' => 'GV_mod_auth_token_passphrase', - 'comment' => _('Auth_token passphrase'), - 'help' => _('Defined in Apache configuration'), - 'default' => false - ), array( 'type' => \registry::TYPE_STRING, 'name' => 'GV_PHP_INI', diff --git a/lib/conf.d/configuration.yml b/lib/conf.d/configuration.yml index 2e3eb81901..75b6126844 100644 --- a/lib/conf.d/configuration.yml +++ b/lib/conf.d/configuration.yml @@ -140,4 +140,8 @@ xsendfile: enabled: false type: nginx mapping: [] +h264-pseudo-streaming: + enabled: false + type: nginx + mapping: [] plugins: [] diff --git a/templates/web/common/thumbnail.html.twig b/templates/web/common/thumbnail.html.twig index feec524aff..a361faf776 100644 --- a/templates/web/common/thumbnail.html.twig +++ b/templates/web/common/thumbnail.html.twig @@ -66,7 +66,13 @@ {src:"/include/jslibs/flowplayer/flowplayer-3.2.18.swf", wmode: "transparent"}, {clip:{url:"{{url|url_encode}}",autoPlay: true,autoBuffering:true,provider: "h264streaming",scaling:"fit"}, onError:function(code,message){getNewVideoToken("{{thumbnail.get_sbas_id() ~'_'~thumbnail.get_record_id()}}", this);}, - plugins: {h264streaming: {url: "/include/jslibs/flowplayer/pseudostreaming/flowplayer.pseudostreaming-3.2.13.swf"}} + plugins: { + {% if app['phraseanet.h264-factory'].isH264Enabled() %} + h264streaming: { + url: "/include/jslibs/flowplayer/pseudostreaming/flowplayer.pseudostreaming-3.2.13.swf" + } + {% endif %} + } }); {% elseif record_type == 'FLEXPAPER' %} @@ -172,7 +178,15 @@ flowplayer("preview{{random}}", {src:"/include/jslibs/flowplayer/flowplayer-3.2.18.swf", wmode: "transparent"}, {clip:{url:"{{url}}",autoPlay: true,autoBuffering:true,provider: "h264streaming",scaling:"fit"}, - onError:function(code,message){getNewVideoToken({{thumbnail.get_sbas_id() ~'_'~thumbnail.get_record_id()}}, this);}, plugins: {h264streaming: {url: "/include/jslibs/flowplayer/pseudostreaming/flowplayer.pseudostreaming-3.2.13.swf"}}}); + onError:function(code,message){getNewVideoToken({{thumbnail.get_sbas_id() ~'_'~thumbnail.get_record_id()}}, this);}, + plugins: { + {% if app['phraseanet.h264-factory'].isH264Enabled() %} + h264streaming: { + url: "/include/jslibs/flowplayer/pseudostreaming/flowplayer.pseudostreaming-3.2.13.swf" + } + {% endif %} + } + }); {% elseif record_type == 'FLEXPAPER' %} {% set random = thumbnail.get_random() %} diff --git a/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php b/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php index 749f7288d8..c34b9ff383 100644 --- a/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php +++ b/tests/Alchemy/Tests/Phrasea/Core/Provider/FileServeServiceProviderTest.php @@ -23,6 +23,16 @@ class FileServeServiceProviderTest extends ServiceProviderTestCase 'phraseanet.xsendfile-factory', 'Alchemy\Phrasea\Http\XSendFile\XSendFileFactory' ), + array( + 'Alchemy\Phrasea\Core\Provider\FileServeServiceProvider', + 'phraseanet.h264-factory', + 'Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory' + ), + array( + 'Alchemy\Phrasea\Core\Provider\FileServeServiceProvider', + 'phraseanet.h264', + 'Alchemy\Phrasea\Http\H264PseudoStreaming\H264Interface' + ), ); } diff --git a/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/ApacheTest.php b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/ApacheTest.php new file mode 100644 index 0000000000..da9c3702ae --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/ApacheTest.php @@ -0,0 +1,46 @@ +assertNull($mode->getUrl($pathfile)); + } else { + $this->assertRegExp($expectedRegExp, (string) $mode->getUrl($pathfile)); + } + } + + public function provideMappingsAndFiles() + { + $dir = sys_get_temp_dir().'/to/subdef'; + $file = $dir . '/to/file'; + + if (!is_dir(dirname($file))) { + mkdir(dirname($file), 0777, true); + } + if (!is_file($file)) { + touch($file); + } + + $mapping = array(array( + 'directory' => $dir, + 'mount-point' => 'mp4-videos', + 'passphrase' => '123456', + )); + + return array( + array(array(), null, '/path/to/file'), + array($mapping, null, '/path/to/file'), + array($mapping, '/^\/mp4-videos\/[a-zA-Z0-9]+\/[0-9a-f]+\/to\/file$/', $file), + ); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/H264FactoryTest.php b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/H264FactoryTest.php new file mode 100644 index 0000000000..7c27a74a4c --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/H264FactoryTest.php @@ -0,0 +1,84 @@ +assertInstanceOf('Alchemy\Phrasea\Http\H264PseudoStreaming\H264Factory', $factory); + } + + public function testFactoryWithH264Enable() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new H264Factory($logger, true, 'nginx', $this->getNginxMapping()); + $this->assertInstanceOf('Alchemy\Phrasea\Http\H264PseudoStreaming\H264Interface', $factory->createMode()); + $this->assertTrue($factory->isH264Enabled()); + } + + public function testFactoryWithH264Disabled() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new H264Factory($logger, false, 'nginx',$this->getNginxMapping()); + $this->assertInstanceOf('Alchemy\Phrasea\Http\H264PseudoStreaming\NullMode', $factory->createMode()); + $this->assertFalse($factory->isH264Enabled()); + } + + /** + * @expectedException \Alchemy\Phrasea\Exception\InvalidArgumentException + */ + public function testFactoryWithWrongTypeThrowsAnExceptionIfRequired() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new H264Factory($logger, true, 'wrong-type', $this->getNginxMapping()); + $factory->createMode(true); + } + + public function testFactoryWithWrongTypeDoesNotThrowsAnException() + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $logger->expects($this->once()) + ->method('error') + ->with($this->isType('string')); + + $factory = new H264Factory($logger, true, 'wrong-type', $this->getNginxMapping()); + $this->assertInstanceOf('Alchemy\Phrasea\Http\H264PseudoStreaming\NullMode', $factory->createMode(false)); + } + + /** + * @dataProvider provideTypes + */ + public function testFactoryType($type, $mapping, $classmode) + { + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $factory = new H264Factory($logger, true, $type, $mapping); + $this->assertInstanceOf($classmode, $factory->createMode()); + } + + public function provideTypes() + { + return array( + array('nginx', $this->getNginxMapping(), 'Alchemy\Phrasea\Http\H264PseudoStreaming\Nginx'), + array('apache', $this->getNginxMapping(), 'Alchemy\Phrasea\Http\H264PseudoStreaming\Apache'), + array('apache2', $this->getNginxMapping(), 'Alchemy\Phrasea\Http\H264PseudoStreaming\Apache'), + ); + } + + private function getNginxMapping() + { + return array(array( + 'directory' => __DIR__ . '/../../../../files/', + 'mount-point' => '/protected/', + 'passphrase' => 'dfdskqhfsfilddsmfmqsdmlfomqs', + )); + } +} diff --git a/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/NginxTest.php b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/NginxTest.php new file mode 100644 index 0000000000..f41bfe3bbc --- /dev/null +++ b/tests/Alchemy/Tests/Phrasea/Http/H264PseudoStreaming/NginxTest.php @@ -0,0 +1,46 @@ +assertNull($mode->getUrl($pathfile)); + } else { + $this->assertRegExp($expectedRegExp, (string) $mode->getUrl($pathfile)); + } + } + + public function provideMappingsAndFiles() + { + $dir = sys_get_temp_dir().'/to/subdef'; + $file = $dir . '/to/file'; + + if (!is_dir(dirname($file))) { + mkdir(dirname($file), 0777, true); + } + if (!is_file($file)) { + touch($file); + } + + $mapping = array(array( + 'directory' => $dir, + 'mount-point' => 'mp4-videos', + 'passphrase' => '123456', + )); + + return array( + array(array(), null, '/path/to/file'), + array($mapping, null, '/path/to/file'), + array($mapping, '/^\/mp4-videos\/to\/file\?hash=[a-zA-Z0-9-_+]+&expires=[0-9]+/', $file), + ); + } +} From 543b9432856a63ef07772178fb84d4d342d2bd69 Mon Sep 17 00:00:00 2001 From: Romain Neutron Date: Thu, 27 Feb 2014 16:00:51 +0100 Subject: [PATCH 7/7] Bump to version 3.8.4-alpha.1 --- lib/Alchemy/Phrasea/Core/Version.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Alchemy/Phrasea/Core/Version.php b/lib/Alchemy/Phrasea/Core/Version.php index 574b34254a..e1192b6e08 100644 --- a/lib/Alchemy/Phrasea/Core/Version.php +++ b/lib/Alchemy/Phrasea/Core/Version.php @@ -18,7 +18,7 @@ namespace Alchemy\Phrasea\Core; */ class Version { - protected static $number = '3.8.4-alpha.0'; + protected static $number = '3.8.4-alpha.1'; protected static $name = 'Diplodocus'; public static function getNumber()