diff --git a/.gitignore b/.gitignore
index 51dccc8..358fc8e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,6 @@ app/config/parameters.yml
composer.phar
behat.yml
+.idea
+!web/uploads/.gitkeep
+!web/uploads/media/.gitkeep
diff --git a/README.md b/README.md
index 5f3cc3b..5bb9512 100644
--- a/README.md
+++ b/README.md
@@ -56,3 +56,8 @@ old view before 1.5 release
## License
All what you can find at this repo shared under the MIT License
+
+## about filestack
+As image uploader we use https://www.filestack.com/.
+To use it You should visit https://dev.filestack.com/apps/ and create new app.
+After that You should put your api_key to parameters.yml as filestack.api_key
diff --git a/app/config/parameters.circle.yml b/app/config/parameters.circle.yml
index be32adb..0ceeaf2 100644
--- a/app/config/parameters.circle.yml
+++ b/app/config/parameters.circle.yml
@@ -20,4 +20,8 @@ parameters:
# default email for bot from which email is sent
from_email: bot@email.com
- admin_password: admin
\ No newline at end of file
+ admin_password: admin
+ #filestack params
+ media.uploadPath: %kernel.root_dir%/../web/uploads/media/
+ media.uploadUrl: /uploads/media/
+ filestack.api_key: A3w1aZwZmQBibC09rAcZnz
diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist
index b989851..4bc4227 100644
--- a/app/config/parameters.yml.dist
+++ b/app/config/parameters.yml.dist
@@ -21,3 +21,7 @@ parameters:
from_email: bot@email.com
admin_password: admin
+
+ media.uploadPath: %kernel.root_dir%/../web/uploads/media/
+ media.uploadUrl: /uploads/media/
+ filestack.api_key: A3w1aZwZmQBibC09rAcZnz
diff --git a/behat.yml.dist b/behat.yml.dist
index d335998..36a8d57 100644
--- a/behat.yml.dist
+++ b/behat.yml.dist
@@ -13,6 +13,7 @@ backend:
browser_name: chrome
goutte: ~
selenium2: ~
+ files_path: %behat.paths.base%/web/uploads/media/
Behat\Symfony2Extension\Extension:
mink_driver: true
diff --git a/circle.yml b/circle.yml
index d0a97d3..19d20af 100644
--- a/circle.yml
+++ b/circle.yml
@@ -33,6 +33,7 @@ test:
pre:
- chmod -R 777 app/cache
- chmod -R 777 app/logs
+ - chmod -R 777 web/uploads/media
- wget http://selenium-release.storage.googleapis.com/2.48/selenium-server-standalone-2.48.2.jar
- nohup bash -c "java -jar selenium-server-standalone-2.48.2.jar &"
override:
diff --git a/features/backend/media.feature b/features/backend/media.feature
new file mode 100644
index 0000000..4fdd2eb
--- /dev/null
+++ b/features/backend/media.feature
@@ -0,0 +1,181 @@
+@backend
+Feature: Media settings
+ In order to update media settings
+ As an admin
+ I should be able to edit media settings
+
+Background:
+ Given following "Event":
+ | ref | title | description | startDate | endDate | venue | email | host |
+ | event | My event | My another awesome event! | 2016-03-01 10:00 | 2016-03-01 18:00 | Burj Khalifa Tower | eventator@email.com | http://localhost:8000 |
+ | event2 | My other event | My other awesome event! | 2016-03-01 10:00 | 2016-03-01 18:00 | Burj Khalifa Tower | eventator@gmail.com | http://eventator.loc:8080 |
+ | event3 | His event | His another awesome event! | 2016-04-01 10:00 | 2016-04-01 18:00 | Kuala-lumpur Tower | eventator@gmail.com | http://event.com |
+ And following "EventTranslation":
+ | event | locale |
+ | event | ru_RU |
+ | event | de_DE |
+ | event2 | ru_RU |
+ | event2 | de_DE |
+ | event3 | ru_RU |
+ And following "Organizer":
+ | ref | title | description | isActive | events |
+ | organizer | My organizer | My another awesome organizer! | 1 | event,event2,event3 |
+ | organizer2 | His organizer | His another awesome organizer! | 1 | event2,event3 |
+ And following "OrganizerTranslation":
+ | organizer | locale |
+ | organizer | ru_RU |
+ | organizer2 | de_DE |
+ And following "Speech":
+ | ref | title | description | language | event |
+ | speech | symfony propagation | world symfony expansion | ru | event |
+ | speech2 | php servers piece | php most popular language | en | event |
+ | speech3 | doctrine must have | what you docrtine project should have | en | event |
+ | speech4 | symfony propagation2 | world symfony expansion2 | en | event2 |
+ | speech5 | php servers piece2 | php most popular language2 | en | event2 |
+ | speech6 | doctrine must have2 | what you docrtine project should have2 | en | event2 |
+ | speech7 | symfony propagation2 | world symfony expansion2 | en | event3 |
+ | speech8 | php servers piece2 | php most popular language2 | en | event3 |
+ | speech9 | doctrine must have2 | what you docrtine project should have2 | en | event3 |
+
+ And following "SpeechTranslation":
+ | speech | locale |
+ | speech | ru_RU |
+ | speech2 | de_DE |
+ | speech3 | de_DE |
+ | speech4 | ru_RU |
+ | speech5 | de_DE |
+ | speech6 | de_DE |
+ | speech7 | ru_RU |
+ | speech8 | de_DE |
+ | speech9 | de_DE |
+ And following "Program":
+ | ref | title | isTopic | isActive | startDate | endDate | events | speech |
+ | program | keynote | 1 | 1 | 2016-03-01 10:00 | 2016-03-01 10:30 | event | |
+ | program2 | alex_symfony | 0 | 1 | 2016-03-01 10:30 | 2016-03-01 11:30 | event | speech |
+ | program3 | phil_php | 0 | 1 | 2016-03-01 11:30 | 2016-03-01 12:30 | event | speech2 |
+ | program4 | coffee1 | 1 | 1 | 2016-03-01 12:30 | 2016-03-01 13:00 | event | |
+ | program5 | phil_doctrine | 0 | 1 | 2016-03-01 13:00 | 2016-03-01 14:30 | event | speech3 |
+ | program6 | end_keynote | 1 | 1 | 2016-03-01 14:30 | 2016-03-01 15:00 | event | |
+ | program7 | after_party | 1 | 1 | 2016-03-01 15:00 | 2016-03-01 18:00 | event | |
+ | program8 | keynote | 1 | 1 | 2016-04-01 10:00 | 2016-04-01 10:30 | event2 | |
+ | program9 | alex_symfony | 0 | 1 | 2016-04-01 10:30 | 2016-04-01 11:30 | event2 | speech4 |
+ | program10 | phil_php | 0 | 1 | 2016-04-01 11:30 | 2016-04-01 12:30 | event2 | speech5 |
+ | program11 | coffee1 | 1 | 1 | 2016-04-01 12:30 | 2016-04-01 13:00 | event2 | |
+ | program12 | phil_doctrine | 0 | 1 | 2016-04-01 13:00 | 2016-04-01 14:30 | event2 | speech6 |
+ | program13 | end_keynote | 1 | 1 | 2016-04-01 14:30 | 2016-04-01 15:00 | event2 | |
+ | program14 | after_party | 1 | 1 | 2016-04-01 15:00 | 2016-04-01 18:00 | event2 | |
+ | program15 | keynote | 1 | 1 | 2016-04-01 10:00 | 2016-04-01 10:30 | event3 | |
+ | program16 | alex_symfony | 0 | 1 | 2016-04-01 10:30 | 2016-04-01 11:30 | event3 | speech7 |
+ | program17 | phil_php | 0 | 1 | 2016-04-01 11:30 | 2016-04-01 12:30 | event3 | speech8 |
+ | program18 | coffee1 | 1 | 1 | 2016-04-01 12:30 | 2016-04-01 13:00 | event3 | |
+ | program19 | phil_doctrine | 0 | 1 | 2016-04-01 13:00 | 2016-04-01 14:30 | event3 | speech9 |
+ | program20 | end_keynote | 1 | 1 | 2016-04-01 14:30 | 2016-04-01 15:00 | event3 | |
+ | program21 | after_party | 1 | 1 | 2016-04-01 15:00 | 2016-04-01 18:00 | event3 | |
+ And following "ProgramTranslation":
+ | program | locale |
+ | program | ru_RU |
+ | program2 | de_DE |
+ | program3 | de_DE |
+ | program4 | de_DE |
+ | program5 | de_DE |
+ | program6 | de_DE |
+ | program7 | de_DE |
+ | program8 | de_DE |
+ | program9 | de_DE |
+ | program10 | de_DE |
+ | program11 | de_DE |
+ | program12 | de_DE |
+ | program13 | de_DE |
+ | program14 | de_DE |
+ | program15 | de_DE |
+ | program16 | de_DE |
+ | program17 | de_DE |
+ | program18 | de_DE |
+ | program19 | de_DE |
+ | program20 | de_DE |
+ | program21 | de_DE |
+ And following "Speaker":
+ | ref | firstName | lastName | Company | email | homepage | twitter | events | speeches |
+ | speaker | Phill | Pilow | Reseach Supplier | | | | event,event2,event3 | speech2,speech3,speech5,speech6,speech8,speech9 |
+ | speaker2 | Alex | Demchenko | KnpLabs | | http://451f.com.ua | https://twitter.com/twitter | event,event2,event3 | speech,speech4,speech7 |
+ And following "SpeakerTranslation":
+ | speaker | locale |
+ | speaker | ru_RU |
+ | speaker2 | de_DE |
+ And following "Sponsor":
+ | ref | company | description | homepage | type | isActive | events |
+ | sponsor | Reseach Supplier | NASA research center | http://nasa.gov.us | 1 | 1 | event,event2,event3 |
+ | sponsor2 | KnpLabs | Happy awesome developer | http://knplabs.com | 2 | 1 | event |
+ And following "SponsorTranslation":
+ | sponsor | locale |
+ | sponsor | de_DE |
+ | sponsor2 | de_DE |
+ And following "Media":
+ | title | filename | created_date | updated_date |
+ | Media1 | logo.png | 2017-10-27 16:02 | 2017-10-27 16:02 |
+ | Media2 | logo.png | 2017-10-27 17:02 | 2017-10-27 17:02 |
+
+@javascript
+Scenario: Admin should have access to the media manage
+ Given I am sign in as admin
+ When I click "Media"
+ Then I wait for a form
+ Then I should see "Add Media"
+ And I should see the row containing "1;Media1"
+ And I should see the row containing "2;Media2"
+ When I click "Edit" on the row containing "1;Media1"
+ Then I wait for a form
+ Then I should see "Media settings"
+
+@javascript
+Scenario: Admin should be able to add media
+ Given I am sign in as admin
+ When I click "Media"
+ Then I wait for a form
+ Then I should see "Add Media"
+ And I click "Add Media"
+ Then I wait for a form
+ Then I should see "Media settings"
+ And I fill in "Title" with "Media3"
+ And I press "Upload file"
+ Then I wait "10" seconds
+ Then I should see " My Device "
+ And I attach the file "test.png" to "fsp-fileUpload"
+ Then I should see "Edit Image"
+ Then I wait "15" seconds
+ And I click on the element with css selector ".fsp-button--primary"
+ Then I wait "10" seconds
+ And I press "Add"
+ Then I wait for a form
+ Then I should see "Media Media3 added."
+ Then I should see the row containing "3;Media3"
+
+@javascript
+Scenario: Admin should be able to update media settings
+ Given I am sign in as admin
+ When I click "Media"
+ Then I wait for a form
+ Then I should see "Add Media"
+ And I should see the row containing "2;Media2"
+ When I click "Edit" on the row containing "2;Media2"
+ Then I wait for a form
+ Then I should see "Media settings"
+ And I fill in "Title" with "Media2_1"
+ And I fill in "Media Description" with "Awesome media"
+ And I fill in "Copyright Info" with "Test media"
+ And I press "Update"
+ Then I wait for a form
+ Then I should see "Media Media2_1 updated."
+ Then I should see the row containing "2;Media2_1"
+
+@javascript
+Scenario: Admin should be delete to the media
+ Given I am sign in as admin
+ When I click "Media"
+ Then I wait for a form
+ Then I should see "Add Media"
+ And I should see the row containing "1;Media1"
+ Then I delete the record with id "1"
+ Then I wait for a form
+ Then I should see "Media deleted."
+ Then I should not see the row containing "1;Media1"
diff --git a/src/Event/EventBundle/Controller/Backend/MediaController.php b/src/Event/EventBundle/Controller/Backend/MediaController.php
new file mode 100644
index 0000000..0c7a55e
--- /dev/null
+++ b/src/Event/EventBundle/Controller/Backend/MediaController.php
@@ -0,0 +1,121 @@
+container->getParameter('media.uploadPath');
+ }
+
+ public function getUploadUrl(){
+ return $this->container->getParameter('media.uploadUrl');
+ }
+
+ public function indexAction()
+ {
+ return $this->render('EventEventBundle:Backend/Media:index.html.twig', array(
+ 'media' => $this->getRepository('EventEventBundle:Media')->findAll(),
+ 'uploadPath' => $this->getUploadPath(),
+ 'uploadUrl' => $this->getUploadUrl()
+ ));
+ }
+
+ public function manageAction(Request $request, $id = null)
+ {
+ $oldFileName = '';
+ if ($id === null) {
+ $media = new Media();
+ } else {
+ $media = $this->findOr404('EventEventBundle:Media', $id);
+ $oldFileName = $media->getFilename();
+ }
+
+ $form = $this->createForm(MediaType::class, $media);
+
+ if ($request->getMethod() === 'POST') {
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ $media->setUpdated(new \DateTime());
+
+ if ($oldFileName !== $media->getFilename()) {
+ $url = $request->request->get('file_url');
+ $path = $this->getUploadPath();
+ $media->setFilename(time() . '_' . $media->getFilename());
+ $file = $this->getFile($url);
+
+ $fs = new Filesystem();
+
+ try {
+ $fs->dumpFile($path . $media->getFilename(), $file, 0777);
+ } catch (IOExceptionInterface $e) {
+ throw new Exception($e->getMessage());
+ }
+ }
+
+ $this->getManager()->persist($media);
+ $this->getManager()->flush();
+
+ $successFlashText = sprintf('Media %s updated.', $media->getTitle());
+ if (!$id) {
+ $successFlashText = sprintf('Media %s added.', $media->getTitle());
+ }
+ $this->setSuccessFlash($successFlashText);
+
+ return $this->redirectToRoute('backend_media');
+ }
+ }
+
+ return $this->render('EventEventBundle:Backend/Media:manage.html.twig', [
+ 'media' => $media,
+ 'form' => $form->createView(),
+ 'fs_api' => $this->container->getParameter('filestack.api_key'),
+ ]);
+ }
+
+ protected function checkAccessFolder($path){
+ if(is_dir($path)){
+ chmod($path, 0777);
+ } else {
+ mkdir($path, 0777, true);
+ }
+ }
+
+ protected function getFile($url)
+ {
+ $client = new Client();
+ $file = $client->createRequest('GET', $url)->send()->getBody(true);
+
+ return $file;
+ }
+
+ public function deleteAction($id)
+ {
+ $this->isGrantedAdmin();
+ $translator = $this->get('translator');
+
+ $entity = $this->findOr404('EventEventBundle:Media', $id);
+ $file = $this->getUploadPath() . $entity->getFilename();
+ $this->getManager()->remove($entity);
+ $this->getManager()->flush();
+ if (is_file($file)) {
+ unlink($file);
+ }
+
+ $this->setSuccessFlash($translator->trans('Media deleted.'));
+
+ return $this->redirectToRoute('backend_media');
+ }
+}
diff --git a/src/Event/EventBundle/Entity/Media.php b/src/Event/EventBundle/Entity/Media.php
new file mode 100644
index 0000000..a508544
--- /dev/null
+++ b/src/Event/EventBundle/Entity/Media.php
@@ -0,0 +1,268 @@
+getId())){
+ $this->setCreated(new \DateTime());
+ }
+ $this->setUpdated(new \DateTime());
+ }
+
+ /**
+ * Get id
+ *
+ * @return integer
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Set title
+ *
+ * @param string $title
+ * @return Media
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+
+ return $this;
+ }
+
+ /**
+ * Get title
+ *
+ * @return string
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Set filename
+ *
+ * @param string $filename
+ * @return Media
+ */
+ public function setFilename($filename)
+ {
+ $this->filename = $filename;
+
+ return $this;
+ }
+
+ /**
+ * Get filename
+ *
+ * @return string
+ */
+ public function getFilename()
+ {
+ return $this->filename;
+ }
+
+ /**
+ * Set description
+ *
+ * @param string $description
+ * @return Media
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+
+ return $this;
+ }
+
+ /**
+ * Get description
+ *
+ * @return string
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Set copyrightInfo
+ *
+ * @param string $copyrightInfo
+ * @return Media
+ */
+ public function setCopyrightInfo($copyrightInfo)
+ {
+ $this->copyrightInfo = $copyrightInfo;
+
+ return $this;
+ }
+
+ /**
+ * Get copyrightInfo
+ *
+ * @return string
+ */
+ public function getCopyrightInfo()
+ {
+ return $this->copyrightInfo;
+ }
+
+ /**
+ * Set mediaCredits
+ *
+ * @param string $mediaCredits
+ * @return Media
+ */
+ public function setMediaCredits($mediaCredits)
+ {
+ $this->mediaCredits = $mediaCredits;
+
+ return $this;
+ }
+
+ /**
+ * Get mediaCredits
+ *
+ * @return string
+ */
+ public function getMediaCredits()
+ {
+ return $this->mediaCredits;
+ }
+
+ /**
+ * Set created
+ *
+ * @param \DateTime $created
+ * @return Media
+ */
+ public function setCreated($created)
+ {
+ if (!$created instanceOf \DateTime) {
+ $created = \DateTime::createFromFormat('m/d/Y', $created);
+ }
+
+ $this->created = $created;
+
+ return $this;
+ }
+
+ /**
+ * Get created
+ *
+ * @return \DateTime
+ */
+ public function getCreated()
+ {
+ return $this->created;
+ }
+
+ /**
+ * Set updated
+ *
+ * @param \DateTime $updated
+ * @return Media
+ */
+ public function setUpdated($updated)
+ {
+ if (!$updated instanceOf \DateTime) {
+ $updated = \DateTime::createFromFormat('m/d/Y', $updated);
+ }
+
+ $this->updated = $updated;
+
+ return $this;
+ }
+
+ /**
+ * Get updated
+ *
+ * @return \DateTime
+ */
+ public function getUpdated()
+ {
+ return $this->updated;
+ }
+
+ public function __toString()
+ {
+ return $this->title;
+ }
+}
diff --git a/src/Event/EventBundle/Entity/Repository/MediaRepository.php b/src/Event/EventBundle/Entity/Repository/MediaRepository.php
new file mode 100644
index 0000000..fbd17d1
--- /dev/null
+++ b/src/Event/EventBundle/Entity/Repository/MediaRepository.php
@@ -0,0 +1,11 @@
+add('title', TextType::class)
+ ->add('filename', HiddenType::class, ['required' => false])
+ ->add('description', TextareaType::class, [
+ 'label' => 'Media Description',
+ 'attr' => array('class' => 'input-xxlarge', 'rows' => 3),
+ 'required' => false
+ ])
+ ->add('copyrightInfo', TextareaType::class, [
+ 'label' => 'Copyright Info',
+ 'attr' => array('class' => 'input-xxlarge', 'rows' => 3),
+ 'required' => false
+ ])
+ ->add('mediaCredits', TextareaType::class, [
+ 'label' => 'Media Credits',
+ 'attr' => array('class' => 'input-xxlarge', 'rows' => 3),
+ 'required' => false
+ ])
+ ;
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults([
+ 'data_class' => 'Event\EventBundle\Entity\Media'
+ ]);
+ }
+
+ public function getBlockPrefix()
+ {
+ return 'media';
+ }
+}
diff --git a/src/Event/EventBundle/Menu/Builder.php b/src/Event/EventBundle/Menu/Builder.php
index d714cf4..ad41e31 100644
--- a/src/Event/EventBundle/Menu/Builder.php
+++ b/src/Event/EventBundle/Menu/Builder.php
@@ -34,6 +34,7 @@ public function sideBar(FactoryInterface $factory, array $options)
$event->addChild('Sponsors', array('route' => 'backend_sponsor'));
$event->addChild('Organizers', array('route' => 'backend_organizer'));
$event->addChild('Calls For Paper', array('route' => 'backend_call_for_paper'));
+ $event->addChild('Media', array('route' => 'backend_media'));
return $menu;
}
diff --git a/src/Event/EventBundle/Resources/config/routing/backend.yml b/src/Event/EventBundle/Resources/config/routing/backend.yml
index ed61cb5..04bdec7 100644
--- a/src/Event/EventBundle/Resources/config/routing/backend.yml
+++ b/src/Event/EventBundle/Resources/config/routing/backend.yml
@@ -169,3 +169,22 @@ backend_call_for_paper_delete:
id: \d+
options:
expose: true
+
+# Media
+backend_media:
+ path: /media
+ defaults: { _controller: EventEventBundle:Backend/Media:index }
+
+backend_media_add:
+ path: /media/add
+ defaults: { _controller: EventEventBundle:Backend/Media:manage }
+
+backend_media_edit:
+ path: /media/edit/{id}
+ defaults: { _controller: EventEventBundle:Backend/Media:manage, id: null }
+ requirements: { id: \d+ }
+
+backend_media_delete:
+ path: /media/delete/{id}
+ defaults: { _controller: EventEventBundle:Backend/Media:delete }
+ requirements: { id: \d+ }
diff --git a/src/Event/EventBundle/Resources/public/css/style.css b/src/Event/EventBundle/Resources/public/css/style.css
index 982da99..23ec902 100644
--- a/src/Event/EventBundle/Resources/public/css/style.css
+++ b/src/Event/EventBundle/Resources/public/css/style.css
@@ -128,3 +128,13 @@ form input {
.events-list {
text-align: right;
}
+
+/* backend media uploader */
+.uploader-container {
+ margin: 10px 0;
+}
+
+#media_title {
+ width: 530px;
+}
+/* backend media uploader */
\ No newline at end of file
diff --git a/src/Event/EventBundle/Resources/views/Backend/Media/index.html.twig b/src/Event/EventBundle/Resources/views/Backend/Media/index.html.twig
new file mode 100644
index 0000000..c09f9f1
--- /dev/null
+++ b/src/Event/EventBundle/Resources/views/Backend/Media/index.html.twig
@@ -0,0 +1,91 @@
+{% extends 'EventEventBundle:Backend:layout.html.twig' %}
+
+{% block content %}
+{{ 'Media'|trans }}
+ {{ 'Add Media'|trans }}
+
+
+
+
+
+
+
+ {% for file in media %}
+ {{ 'ID'|trans }}
+ {{ 'Thumbnail'|trans }}
+ {{ 'Title'|trans }}
+ {{ 'Created'|trans }}
+ {{ 'Updated'|trans }}
+ {{ 'Actions'|trans }}
+
+
+ {% else %}
+ {{ file.id }}
+
+
+
+
+
+ {{ file.title }}
+
+
+ {{ file.created|date }}
+ {{ file.updated|date }}
+
+
+
+
+ {% endfor %}
+
+ No media found.
+ Media settings
+ ← back
+