diff --git a/Controller/Sequence/SequenceController.php b/Controller/Sequence/SequenceController.php
new file mode 100644
index 000000000..b5d3e40be
--- /dev/null
+++ b/Controller/Sequence/SequenceController.php
@@ -0,0 +1,109 @@
+container->get('security.context')->isGranted('OPEN', $resource->getResourceNode())) {
+ throw new AccessDeniedException();
+ }
+
+ return $this->render('UJMExoBundle:Sequence:view.html.twig', array(
+ '_resource' => $resource
+ )
+ );
+ }
+
+ /**
+ * administrate an exercise player
+ * @Route("/edit/{id}", requirements={"id" = "\d+"}, name="ujm_sequence_administrate")
+ * @Method("GET")
+ * @ParamConverter("Sequence", class="UJMExoBundle:Sequence\Sequence")
+ */
+ public function administrateAction(Sequence $resource) {
+ if (false === $this->container->get('security.context')->isGranted('ADMINISTRATE', $resource->getResourceNode())) {
+ throw new AccessDeniedException();
+ }
+
+ $steps = $this->get('ujm_exo_bundle.manager.steps')->getSteps($resource);
+
+ return $this->render('UJMExoBundle:Sequence:edit.html.twig', array(
+ '_resource' => $resource,
+ 'steps' => $steps
+ )
+ );
+ }
+
+ /**
+ * update an exercise player
+ * @Route("/update/{id}", requirements={"id" = "\d+"}, name="ujm_sequence_update", options = {"expose" = true})
+ * @Method("PUT")
+ * @ParamConverter("Sequence", class="UJMExoBundle:Sequence\Sequence")
+ *
+ */
+ public function updateAction(Sequence $resource) {
+
+ if (false === $this->container->get('security.context')->isGranted('EDIT', $resource->getResourceNode())) {
+ throw new AccessDeniedException();
+ }
+
+ $params = array(
+ 'method' => 'PUT',
+ 'csrf_protection' => false,
+ );
+ // Create form
+ $form = $this->container->get('form.factory')->create('sequence_type', $resource, $params);
+ $request = $this->container->get('request');
+ $form->submit($request);
+ $response = array();
+ if ($form->isValid()) {
+ $resource = $form->getData();
+ $updated = $this->get('ujm_exo_bundle.manager.sequence')->update($resource);
+ $response['status'] = 'success';
+ $response['messages'] = array();
+ $response['data'] = $updated;
+ } else {
+ $errors = $this->getFormErrors($form);
+ $response['status'] = 'error';
+ $response['messages'] = $errors;
+ $response['data'] = null;
+ }
+ return new JsonResponse($response);
+ }
+
+ private function getFormErrors(FormInterface $form) {
+ $errors = array();
+ foreach ($form->getErrors() as $key => $error) {
+ $errors[$key] = $error->getMessage();
+ }
+ // Get errors from children
+ foreach ($form->all() as $child) {
+ if (!$child->isValid()) {
+ $errors[$child->getName()] = $this->getFormErrors($child);
+ }
+ }
+ return $errors;
+ }
+
+}
diff --git a/Controller/Sequence/StepController.php b/Controller/Sequence/StepController.php
new file mode 100644
index 000000000..004a1d79e
--- /dev/null
+++ b/Controller/Sequence/StepController.php
@@ -0,0 +1,54 @@
+container->get('security.context')->isGranted('EDIT', $resource->getResourceNode())) {
+ throw new AccessDeniedException();
+ }
+ $request = $this->container->get('request');
+ // get request data
+ $steps = $request->request->get('steps');
+
+ // response
+ $response = array();
+
+ // update the exercise player pages
+ try {
+ $updated = $this->get('ujm_exo_bundle.manager.steps')->updateSteps($resource, $steps);
+ $response['status'] = 'success';
+ $response['messages'] = array();
+ $response['data'] = $updated;
+ } catch (\Exception $ex) {
+ $response['status'] = 'error';
+ $response['messages'] = $ex->getMessage();
+ $response['data'] = null;
+ }
+ return new JsonResponse($response);
+ }
+
+
+
+}
diff --git a/DependencyInjection/UJMExoExtension.php b/DependencyInjection/UJMExoExtension.php
index 77fcc4465..48e64ffbe 100755
--- a/DependencyInjection/UJMExoExtension.php
+++ b/DependencyInjection/UJMExoExtension.php
@@ -15,5 +15,6 @@ public function load(array $configs, ContainerBuilder $container)
$loader = new YamlFileLoader($container, $locator);
$loader->load('services.yml');
$loader->load('parameters.yml');
+ $loader->load('form_types.yml');
}
}
diff --git a/Entity/Sequence/Sequence.php b/Entity/Sequence/Sequence.php
new file mode 100644
index 000000000..ab2e9f68b
--- /dev/null
+++ b/Entity/Sequence/Sequence.php
@@ -0,0 +1,195 @@
+startDate = new \DateTime();
+ $this->steps = new ArrayCollection();
+ }
+
+ /**
+ * Get sequence Id
+ * @return integer
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ *
+ * @param Step $s
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function addStep(Step $s) {
+ $this->steps[] = $s;
+ return $this;
+ }
+
+ /**
+ *
+ * @param Step $s
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function removePage(Step $s) {
+ $this->steps->removeElement($s);
+ return $this;
+ }
+
+ /**
+ *
+ * @return ArrayCollection
+ */
+ public function getSteps() {
+ return $this->steps;
+ }
+
+ /**
+ * Set sequence name
+ * @param string $name
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function setName($name) {
+ $this->name = $name;
+ return $this;
+ }
+
+ /**
+ * Get sequence name
+ * @return string
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * Set sequence startDate
+ *
+ * @param datetime $startDate
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function setStartDate($startDate) {
+ $this->startDate = $startDate;
+ return $this;
+ }
+
+ /**
+ * Get sequence startDate
+ *
+ * @return datetime
+ */
+ public function getStartDate() {
+ return $this->startDate;
+ }
+
+ /**
+ * Set sequence endDate
+ *
+ * @param datetime $endDate
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function setEndDate($endDate) {
+ $this->endDate = $endDate;
+ return $this;
+ }
+
+ /**
+ * Get sequence endDate
+ *
+ * @return datetime
+ */
+ public function getEndDate() {
+ return $this->endDate;
+ }
+
+
+ /**
+ * Set sequence description
+ *
+ * @param text $description
+ * @return \UJM\ExoBundle\Entity\Sequence\Sequence
+ */
+ public function setDescription($description) {
+ $this->description = $description;
+ return $this;
+ }
+
+ /**
+ * Get sequence description
+ *
+ * @return text
+ */
+ public function getDescription() {
+ return $this->description;
+ }
+
+ public function jsonSerialize() {
+
+ return array(
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'description' => $this->description,
+ 'startDate' => !empty($this->startDate) ? $this->startDate->format('Y-m-d'): null,
+ 'endDate' => !empty($this->endDate) ? $this->endDate->format('Y-m-d'): null
+ );
+ }
+
+}
diff --git a/Entity/Sequence/Step.php b/Entity/Sequence/Step.php
new file mode 100644
index 000000000..95eaad128
--- /dev/null
+++ b/Entity/Sequence/Step.php
@@ -0,0 +1,209 @@
+shuffle = false;
+ $this->isFirst = false;
+ $this->isLast = false;
+ }
+
+ /**
+ * Get page Id
+ * @return integer
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ public function getDescription(){
+ return $this->description;
+ }
+
+ /**
+ *
+ * @param string $description
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setDescription($description){
+ $this->description = $description;
+ return $this;
+ }
+
+ /**
+ *
+ * @param Sequence $sequence
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setSequence(Sequence $sequence){
+ $this->sequence = $sequence;
+ return $this;
+ }
+
+ /**
+ *
+ * @return sequence
+ */
+ public function getSequence(){
+ return $this->sequence;
+ }
+
+ /**
+ *
+ * @param integer $position
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setPosition($position) {
+ $this->position = $position;
+ return $this;
+ }
+
+ /**
+ *
+ * @return integer
+ */
+ public function getPosition(){
+ return $this->position;
+ }
+
+ /**
+ *
+ * @param boolean $shuffle
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setShuffle($shuffle){
+ $this->shuffle = $shuffle;
+ return $this;
+ }
+
+ /**
+ *
+ * @return boolean
+ */
+ public function getShuffle(){
+ return $this->shuffle;
+ }
+
+ /**
+ *
+ * @param boolean $isLast
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setIsLast($isLast){
+ $this->isLast = $isLast;
+ return $this;
+ }
+
+ /**
+ *
+ * @return boolean
+ */
+ public function getIsLast(){
+ return $this->isLast;
+ }
+
+ /**
+ *
+ * @param boolean $isFirst
+ * @return \UJM\ExoBundle\Entity\Sequence\Step
+ */
+ public function setIsFirst($isFirst){
+ $this->isFirst = $isFirst;
+ return $this;
+ }
+
+ /**
+ *
+ * @return boolean
+ */
+ public function getIsFirst(){
+ return $this->isFirst;
+ }
+
+ public function jsonSerialize()
+ {
+ // TODO serialize questions arraycollection
+ return array (
+ 'id' => $this->id,
+ 'position' => $this->position,
+ 'shuffle' => $this->shuffle,
+ 'isFirst' => $this->isFirst,
+ 'isLast' => $this->isLast,
+ 'description' => $this->description,
+ 'sequenceId' => $this->sequence->getId()
+ );
+ }
+
+}
diff --git a/Form/Sequence/SequenceType.php b/Form/Sequence/SequenceType.php
new file mode 100644
index 000000000..8d79b51da
--- /dev/null
+++ b/Form/Sequence/SequenceType.php
@@ -0,0 +1,55 @@
+add(
+ 'name', 'text'
+ )
+ ->add('description', 'tinymce', array(
+ 'attr' => array('data-new-tab' => 'yes'),
+ 'label' => 'Description', 'required' => false
+ )
+ )
+ ->add('startDate', 'datetime', array(
+ 'data' => new \DateTime(),
+ 'attr'=>array('style'=>'display:none;'),
+ 'widget' => 'single_text',
+ 'label' => ' ',
+ 'input' => 'datetime'
+ )
+ )
+ ->add('endDate', 'datetime', array(
+ 'data' => null,
+ 'attr'=>array('style'=>'display:none;'),
+ 'label' => ' ',
+ 'widget' => 'single_text',
+ 'required' => false ,
+ 'input' => 'datetime'
+ )
+ );
+ }
+
+ public function configureOptions(OptionsResolver $resolver)
+ {
+ $resolver->setDefaults(
+ array(
+ 'data_class' => 'UJM\ExoBundle\Entity\Sequence\Sequence',
+ 'translation_domain' => 'ujm_sequence',
+ )
+ );
+ }
+
+ public function getName()
+ {
+ return 'sequence_type';
+ }
+}
diff --git a/Listener/Resource/SequenceListener.php b/Listener/Resource/SequenceListener.php
new file mode 100644
index 000000000..d7f37f61b
--- /dev/null
+++ b/Listener/Resource/SequenceListener.php
@@ -0,0 +1,123 @@
+getResource();
+ $route = $this->container
+ ->get('router')
+ ->generate('ujm_sequence_administrate', array('id' => $resource->getId())
+ );
+ $event->setResponse(new RedirectResponse($route));
+ $event->stopPropagation();
+ }
+
+ /**
+ * Fired when a new ResourceNode of type Sequence is opened
+ * @param \Claroline\CoreBundle\Event\OpenResourceEvent $event
+ * @throws \Exception
+ */
+ public function onOpen(OpenResourceEvent $event) {
+ $resource = $event->getResource();
+ //Redirection to the controller.
+ $route = $this->container
+ ->get('router')
+ ->generate('ujm_sequence_open', array('id' => $resource->getId()));
+ $event->setResponse(new RedirectResponse($route));
+ $event->stopPropagation();
+ }
+
+ /**
+ *
+ * @param CreateResourceEvent $event
+ * @throws \Exception
+ */
+ public function onCreate(CreateResourceEvent $event) {
+ // Create form
+ $form = $this->container->get('form.factory')->create('sequence_type', new Sequence());
+ // Try to process form
+ $request = $this->container->get('request');
+ $form->submit($request);
+ if ($form->isValid()) {
+ $resource = $form->getData();
+
+ $sequence = $this->container->get('ujm_exo_bundle.manager.sequence')->createFirstAndLastStep($resource);
+
+ $event->setResources(array($sequence));
+
+
+ } else {
+ $content = $this->container->get('templating')->render(
+ 'ClarolineCoreBundle:Resource:createForm.html.twig', array(
+ 'form' => $form->createView(),
+ 'resourceType' => 'ujm_sequence'
+ ));
+ $event->setErrorFormContent($content);
+ }
+ $event->stopPropagation();
+ return;
+ }
+
+ /**
+ *
+ * @param CreateFormResourceEvent $event
+ */
+ public function onCreateForm(CreateFormResourceEvent $event) {
+ // Create form
+ $form = $this->container->get('form.factory')->create('sequence_type', new Sequence());
+
+ $content = $this->container->get('templating')->render(
+ 'ClarolineCoreBundle:Resource:createForm.html.twig', array(
+ 'form' => $form->createView(),
+ 'resourceType' => 'ujm_sequence'
+ ));
+
+ $event->setResponseContent($content);
+ $event->stopPropagation();
+ }
+
+ /**
+ * Fired when a ResourceNode of type Sequence is deleted
+ * @param \Claroline\CoreBundle\Event\DeleteResourceEvent $event
+ * @throws \Exception
+ */
+ public function onDelete(DeleteResourceEvent $event) {
+ $em = $this->container->get('doctrine.orm.entity_manager');
+ $resource = $event->getResource();
+ $em->remove($resource);
+ $event->stopPropagation();
+ }
+
+ /**
+ * Fired when a ResourceNode of type Sequence is duplicated
+ * @param \Claroline\CoreBundle\Event\CopyResourceEvent $event
+ * @throws \Exception
+ */
+ public function onCopy(CopyResourceEvent $event) {
+ $toCopy = $event->getResource();
+ $new = new Sequence();
+ $new->setName($toCopy->getName());
+ $event->setCopy($new);
+ $event->stopPropagation();
+ }
+
+}
diff --git a/Manager/Sequence/SequenceManager.php b/Manager/Sequence/SequenceManager.php
new file mode 100644
index 000000000..498a7ea4c
--- /dev/null
+++ b/Manager/Sequence/SequenceManager.php
@@ -0,0 +1,57 @@
+em = $em;
+ $this->translator = $translator;
+ }
+
+ public function getRepository() {
+ return $this->em->getRepository('UJMExoBundle:Sequence\Sequence');
+ }
+
+ public function createFirstAndLastStep(Sequence $s) {
+
+ // add first page
+ $first = new Step();
+ $first->setIsFirst(true);
+ $first->setPosition(1);
+ $first->setDescription('
This is the first Step
');
+ $first->setSequence($s);
+ $s->addStep($first);
+
+ // add last page
+ $last = new Step();
+ $last->setIsLast(true);
+ $last->setPosition(2);
+ $last->setDescription('This is the last Step
');
+ $last->setSequence($s);
+ $s->addStep($last);
+
+ $this->em->persist($s);
+ $this->em->flush();
+ return $s;
+ }
+
+ public function update(Sequence $s) {
+ $this->em->persist($s);
+ $this->em->flush();
+ return $s;
+ }
+
+}
diff --git a/Manager/Sequence/StepManager.php b/Manager/Sequence/StepManager.php
new file mode 100644
index 000000000..09cf8e37c
--- /dev/null
+++ b/Manager/Sequence/StepManager.php
@@ -0,0 +1,125 @@
+em = $em;
+ $this->translator = $translator;
+ }
+
+ public function getRepository() {
+ return $this->em->getRepository('UJMExoBundle:Sequence\Step');
+ }
+
+ /**
+ * Get all steps
+ * @param Sequence $s
+ * @return ArrayCollection
+ */
+ public function getSteps(Sequence $s) {
+ $steps = $this->getRepository()->findBy(array('sequence' => $s), array('position' => 'ASC'));
+ return $steps;
+ }
+
+ /**
+ *
+ * @param Sequence $s
+ * @param type $steps
+ */
+ public function updateSteps(Sequence $s, $steps) {
+
+ // validate data or throws exception
+ $this->validateStepsData($steps);
+
+ // get original pages before update to delete unused steps
+ $oldSteps = $this->getSteps($s);
+ $this->deleteUnusedSteps($oldSteps, $steps);
+
+ foreach ($steps as $step) {
+ $stepEntity = null;
+ $toDelete = false;
+ if (isset($step['id'])) {
+ $stepEntity = $this->getRepository()->findOneBy(array('id' => $step['id']));
+ } else {
+ $stepEntity = new Step();
+ $stepEntity->setSequence($s);
+ }
+ if (!$toDelete) {
+ $stepEntity->setPosition($step['position']);
+ $stepEntity->setDescription($step['description']);
+ $stepEntity->setShuffle(isset($step['shuffle']) ? $step['shuffle'] : false);
+ $this->em->persist($stepEntity);
+ }
+ $this->em->flush();
+ }
+
+ return $this->getSteps($s);
+ }
+
+ /**
+ * Since we get an array from angular service we have to check the received data for each step
+ * @param Array $steps
+ * @return boolean
+ * @throws Exception
+ */
+ private function validateStepsData($steps) {
+ $valid = true;
+
+ if (!$valid) {
+ throw new Exception('error');
+ }
+ return $valid;
+ }
+
+ /**
+ * Compare two Step(s) collection, the old one and the new one
+ * if an item is in the old collection and in the new one we keep it
+ * if an item in the new collection has no id we also keep it
+ * if an item has an id but can not be found in the new collection we remove it
+ * @param ArrayCollection $oldCollection
+ * @param Array $newCollection
+ */
+ private function deleteUnusedSteps($oldCollection, $newCollection){
+ foreach ($oldCollection as $toCheck){
+ $toKeep = false;
+ $currentId = $toCheck->getId();
+ foreach($newCollection as $new){
+ if(!isset($new['id']) || $new['id'] == $currentId){
+ $toKeep = true;
+ break;
+ }
+ }
+ if(!$toKeep){
+ $step = $this->getRepository()->findOneBy(array('id' => $currentId));
+ $this->em->remove($step);
+ $this->em->flush();
+ }
+ }
+ }
+
+ public function addStep(Sequence $s, $step) {
+
+ $stepEntity = new Step();
+ $stepEntity->setExercisePlayer($s);
+ $stepEntity->setPosition($step['position']);
+ $stepEntity->setDescription($step['description']);
+ $stepEntity->setShuffle(isset($step['shuffle']) ? $step['shuffle'] : false);
+ $this->em->persist($stepEntity);
+ $this->em->flush();
+ }
+
+}
diff --git a/Migrations/pdo_mysql/Version20150702121020.php b/Migrations/pdo_mysql/Version20150702121020.php
new file mode 100644
index 000000000..aa850bcc0
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150702121020.php
@@ -0,0 +1,45 @@
+addSql("
+ CREATE TABLE ujm_exercise_player (
+ id INT AUTO_INCREMENT NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ description LONGTEXT DEFAULT NULL,
+ start_date DATETIME NOT NULL,
+ end_date DATETIME DEFAULT NULL,
+ published TINYINT(1) NOT NULL,
+ modified TINYINT(1) NOT NULL,
+ resourceNode_id INT DEFAULT NULL,
+ UNIQUE INDEX UNIQ_746F5F97B87FAB32 (resourceNode_id),
+ PRIMARY KEY(id)
+ ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_exercise_player
+ ADD CONSTRAINT FK_746F5F97B87FAB32 FOREIGN KEY (resourceNode_id)
+ REFERENCES claro_resource_node (id)
+ ON DELETE CASCADE
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ DROP TABLE ujm_exercise_player
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Migrations/pdo_mysql/Version20150703141121.php b/Migrations/pdo_mysql/Version20150703141121.php
new file mode 100644
index 000000000..ad105e90c
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150703141121.php
@@ -0,0 +1,32 @@
+addSql("
+ ALTER TABLE ujm_exercise_player
+ ADD creation DATETIME NOT NULL,
+ ADD modification DATETIME NOT NULL
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ ALTER TABLE ujm_exercise_player
+ DROP creation,
+ DROP modification
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Migrations/pdo_mysql/Version20150703162336.php b/Migrations/pdo_mysql/Version20150703162336.php
new file mode 100644
index 000000000..7dded2c75
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150703162336.php
@@ -0,0 +1,50 @@
+addSql("
+ CREATE TABLE ujm_exercise_page (
+ id INT AUTO_INCREMENT NOT NULL,
+ exercise_player_id INT NOT NULL,
+ position SMALLINT DEFAULT NULL,
+ shuffle TINYINT(1) NOT NULL,
+ is_first_page TINYINT(1) NOT NULL,
+ is_last_page TINYINT(1) NOT NULL,
+ resourceNode_id INT DEFAULT NULL,
+ INDEX IDX_19F33E7D3731F335 (exercise_player_id),
+ UNIQUE INDEX UNIQ_19F33E7DB87FAB32 (resourceNode_id),
+ PRIMARY KEY(id)
+ ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_exercise_page
+ ADD CONSTRAINT FK_19F33E7D3731F335 FOREIGN KEY (exercise_player_id)
+ REFERENCES ujm_exercise_player (id)
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_exercise_page
+ ADD CONSTRAINT FK_19F33E7DB87FAB32 FOREIGN KEY (resourceNode_id)
+ REFERENCES claro_resource_node (id)
+ ON DELETE CASCADE
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ DROP TABLE ujm_exercise_page
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Migrations/pdo_mysql/Version20150706165036.php b/Migrations/pdo_mysql/Version20150706165036.php
new file mode 100644
index 000000000..82ac0253d
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150706165036.php
@@ -0,0 +1,48 @@
+addSql("
+ ALTER TABLE ujm_exercise_page
+ DROP FOREIGN KEY FK_19F33E7DB87FAB32
+ ");
+ $this->addSql("
+ DROP INDEX UNIQ_19F33E7DB87FAB32 ON ujm_exercise_page
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_exercise_page
+ ADD description LONGTEXT DEFAULT NULL,
+ DROP resourceNode_id
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ ALTER TABLE ujm_exercise_page
+ ADD resourceNode_id INT DEFAULT NULL,
+ DROP description
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_exercise_page
+ ADD CONSTRAINT FK_19F33E7DB87FAB32 FOREIGN KEY (resourceNode_id)
+ REFERENCES claro_resource_node (id)
+ ON DELETE CASCADE
+ ");
+ $this->addSql("
+ CREATE UNIQUE INDEX UNIQ_19F33E7DB87FAB32 ON ujm_exercise_page (resourceNode_id)
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Migrations/pdo_mysql/Version20150707164200.php b/Migrations/pdo_mysql/Version20150707164200.php
new file mode 100644
index 000000000..73beda8a9
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150707164200.php
@@ -0,0 +1,36 @@
+addSql("
+ ALTER TABLE ujm_exercise_player
+ DROP published,
+ DROP modified,
+ DROP creation,
+ DROP modification
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ ALTER TABLE ujm_exercise_player
+ ADD published TINYINT(1) NOT NULL,
+ ADD modified TINYINT(1) NOT NULL,
+ ADD creation DATETIME NOT NULL,
+ ADD modification DATETIME NOT NULL
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Migrations/pdo_mysql/Version20150715120410.php b/Migrations/pdo_mysql/Version20150715120410.php
new file mode 100644
index 000000000..452e1d886
--- /dev/null
+++ b/Migrations/pdo_mysql/Version20150715120410.php
@@ -0,0 +1,68 @@
+addSql("
+ CREATE TABLE ujm_sequence_step (
+ id INT AUTO_INCREMENT NOT NULL,
+ sequence_id INT NOT NULL,
+ description LONGTEXT DEFAULT NULL,
+ position SMALLINT DEFAULT NULL,
+ shuffle TINYINT(1) NOT NULL,
+ is_first TINYINT(1) NOT NULL,
+ is_last TINYINT(1) NOT NULL,
+ INDEX IDX_2AE7A31998FB19AE (sequence_id),
+ PRIMARY KEY(id)
+ ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB
+ ");
+ $this->addSql("
+ CREATE TABLE ujm_sequence (
+ id INT AUTO_INCREMENT NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ description LONGTEXT DEFAULT NULL,
+ start_date DATETIME NOT NULL,
+ end_date DATETIME DEFAULT NULL,
+ resourceNode_id INT DEFAULT NULL,
+ UNIQUE INDEX UNIQ_CB11F712B87FAB32 (resourceNode_id),
+ PRIMARY KEY(id)
+ ) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_sequence_step
+ ADD CONSTRAINT FK_2AE7A31998FB19AE FOREIGN KEY (sequence_id)
+ REFERENCES ujm_sequence (id)
+ ");
+ $this->addSql("
+ ALTER TABLE ujm_sequence
+ ADD CONSTRAINT FK_CB11F712B87FAB32 FOREIGN KEY (resourceNode_id)
+ REFERENCES claro_resource_node (id)
+ ON DELETE CASCADE
+ ");
+ }
+
+ public function down(Schema $schema)
+ {
+ $this->addSql("
+ ALTER TABLE ujm_sequence_step
+ DROP FOREIGN KEY FK_2AE7A31998FB19AE
+ ");
+ $this->addSql("
+ DROP TABLE ujm_sequence_step
+ ");
+ $this->addSql("
+ DROP TABLE ujm_sequence
+ ");
+ }
+}
\ No newline at end of file
diff --git a/Resources/config/config.yml b/Resources/config/config.yml
index a011c438f..9d382afa9 100755
--- a/Resources/config/config.yml
+++ b/Resources/config/config.yml
@@ -8,6 +8,14 @@ plugin:
icon: res_exo.png
activity_rules:
- action: resource-ujm_exercise-exercise_evaluated
+ - class: UJM\ExoBundle\Entity\Sequence\Sequence
+ name: ujm_sequence
+ is_exportable: false
+ icon: res_exo.png
+ actions:
+ - name: administrate
+ menu_name: ujm_sequence_administrate
+
tools:
- name: ujm_questions
diff --git a/Resources/config/form_types.yml b/Resources/config/form_types.yml
new file mode 100644
index 000000000..ee8cdabb9
--- /dev/null
+++ b/Resources/config/form_types.yml
@@ -0,0 +1,6 @@
+services:
+ # ExercisePlayer type
+ ujm_exo_bundle.form.type.exercise_player_type:
+ class: %ujm_exo_bundle.form.type.sequence_type.class%
+ tags:
+ - { name: form.type, alias: sequence_type }
diff --git a/Resources/config/parameters.yml b/Resources/config/parameters.yml
index dc2eee835..209410e76 100755
--- a/Resources/config/parameters.yml
+++ b/Resources/config/parameters.yml
@@ -4,3 +4,10 @@
parameters:
ujm.param.exo_directory: "%claroline.param.uploads_directory%/ujmexo"
ujm.param.qti_directory: "%ujm.param.exo_directory%/qti"
+
+ # Form Types
+ ujm_exo_bundle.form.type.sequence_type.class: UJM\ExoBundle\Form\Sequence\SequenceType
+
+ # Managers
+ ujm_exo_bundle.manager.sequence.class: UJM\ExoBundle\Manager\Sequence\SequenceManager
+ ujm_exo_bundle.manager.steps.class: UJM\ExoBundle\Manager\Sequence\StepManager
diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml
index 6520cec8b..4e98b11fd 100755
--- a/Resources/config/routing.yml
+++ b/Resources/config/routing.yml
@@ -49,3 +49,14 @@ all_paper:
all_qti:
resource: "routing/all/qti.yml"
prefix: /all/qti
+
+sequence:
+ resource: "@UJMExoBundle/Controller/Sequence/SequenceController.php"
+ type: annotation
+ prefix: /sequence
+
+
+sequence_steps:
+ resource: "@UJMExoBundle/Controller/Sequence/StepController.php"
+ type: annotation
+ prefix: /page
diff --git a/Resources/config/services.yml b/Resources/config/services.yml
index dc6afcb28..f6fd7ef43 100755
--- a/Resources/config/services.yml
+++ b/Resources/config/services.yml
@@ -177,3 +177,27 @@ services:
scope: request
tags:
- { name: validator.constraint_validator, alias: ujm.exercise_isvalidqcmmark }
+
+ ujm.listener.sequence_listener:
+ class: UJM\ExoBundle\Listener\Resource\SequenceListener
+ calls:
+ - [setContainer, ["@service_container"]]
+ tags:
+ - { name: kernel.event_listener, event: create_form_ujm_sequence, method: onCreateForm }
+ - { name: kernel.event_listener, event: create_ujm_sequence, method: onCreate }
+ - { name: kernel.event_listener, event: open_ujm_sequence, method: onOpen }
+ - { name: kernel.event_listener, event: delete_ujm_sequence, method: onDelete }
+ - { name: kernel.event_listener, event: copy_ujm_sequence, method: onCopy }
+ - { name: kernel.event_listener, event: ujm_sequence_administrate_ujm_sequence, method: onAdministrate }
+
+ ujm_exo_bundle.manager.sequence:
+ class: UJM\ExoBundle\Manager\Sequence\SequenceManager
+ arguments:
+ entityManager: @doctrine.orm.entity_manager
+ translator: @translator
+
+ ujm_exo_bundle.manager.steps:
+ class: UJM\ExoBundle\Manager\Sequence\StepManager
+ arguments:
+ entityManager: @doctrine.orm.entity_manager
+ translator: @translator
\ No newline at end of file
diff --git a/Resources/public/css/exercise-player.css b/Resources/public/css/exercise-player.css
new file mode 100644
index 000000000..7802013e3
--- /dev/null
+++ b/Resources/public/css/exercise-player.css
@@ -0,0 +1 @@
+.exercise-player-container{width:100%;background:white;padding:15px}.exercise-player-container .page-container ul.page-list{list-style-type:none}.exercise-player-container .page-container ul.page-list li.page-list-item{display:inline-block;line-height:50px;width:50px;height:50px;vertical-align:middle;border:1px solid black;cursor:pointer;background-color:rgba(255,255,255,0.8);margin:5px;border-radius:5px}.exercise-player-container .page-container ul.page-list li.page-list-item div{display:inline-block}.exercise-player-container .page-container ul.page-list li.page-list-item.active{background-color:#428bca}.exercise-player-container .page-container ul.page-list .placeholder{display:inline-block;border:1px dotted #aaa;width:35px;line-height:50px;height:35px;vertical-align:middle;border-radius:5px;background-color:rgba(66,139,202,0.2)}
\ No newline at end of file
diff --git a/Resources/public/css/ujm-sequence.css b/Resources/public/css/ujm-sequence.css
new file mode 100644
index 000000000..01ce00b9d
--- /dev/null
+++ b/Resources/public/css/ujm-sequence.css
@@ -0,0 +1 @@
+.ujm-sequence-container{width:100%;background:white;padding:15px}.ujm-sequence-container .step-container ul.step-list{list-style-type:none}.ujm-sequence-container .step-container ul.step-list li.step-list-item{display:inline-block;line-height:50px;width:50px;height:50px;vertical-align:middle;border:1px solid black;cursor:pointer;background-color:rgba(255,255,255,0.8);margin:5px;border-radius:5px}.ujm-sequence-container .step-container ul.step-list li.step-list-item div{display:inline-block}.ujm-sequence-container .step-container ul.step-list li.step-list-item.active{background-color:#428bca}.ujm-sequence-container .step-container ul.step-list .placeholder{display:inline-block;border:1px dotted #aaa;width:35px;line-height:50px;height:35px;vertical-align:middle;border-radius:5px;background-color:rgba(66,139,202,0.2)}
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Sequence/Controllers/SequenceEditCtrl.js b/Resources/public/js/sequence/Sequence/Controllers/SequenceEditCtrl.js
new file mode 100644
index 000000000..421869098
--- /dev/null
+++ b/Resources/public/js/sequence/Sequence/Controllers/SequenceEditCtrl.js
@@ -0,0 +1,75 @@
+(function () {
+ 'use strict';
+
+ angular.module('Sequence').controller('SequenceEditCtrl', [
+ 'SequenceService',
+ function (SequenceService) {
+
+ this.sequence = {};
+ this.isCollapsed = false;
+
+ this.endDateIsOpened = false;
+ this.startDateIsOpened = false;
+
+ this.openEndDate = function ($event) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ this.startDateIsOpened = true;
+ };
+
+ this.openStartDate = function ($event) {
+ $event.preventDefault();
+ $event.stopPropagation();
+ this.endDateIsOpened = true;
+ };
+
+ // not working
+ this.dateOptions = {
+ showButtonBar: false,
+ closeText: 'Close Me',
+ showWeeks: false
+ };
+
+
+
+ // Tiny MCE options
+ this.tinymceOptions = {
+ relative_urls: false,
+ theme: 'modern',
+ browser_spellcheck: true,
+ autoresize_min_height: 100,
+ autoresize_max_height: 500,
+ plugins: [
+ 'autoresize advlist autolink lists link image charmap print preview hr anchor pagebreak',
+ 'searchreplace wordcount visualblocks visualchars fullscreen',
+ 'insertdatetime media nonbreaking save table directionality',
+ 'template paste textcolor emoticons code'
+ ],
+ toolbar1: 'undo redo | styleselect | bold italic underline | forecolor | alignleft aligncenter alignright | preview fullscreen',
+ paste_preprocess: function (plugin, args) {
+ var link = $('' + args.content + '
').text().trim(); //inside div because a bug of jquery
+ var url = link.match(/^(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)|((mailto:)?[_.\w-]+@([\w][\w\-]+\.)+[a-zA-Z]{2,3})$/);
+
+ if (url) {
+ args.content = '' + link + '';
+ window.Claroline.Home.generatedContent(link, function (data) {
+ insertContent(data);
+ }, false);
+ }
+ }
+ };
+
+ this.update = function () {
+ SequenceService.update(this.sequence);
+ };
+
+ this.setSequence = function (sequence) {
+ this.sequence = sequence;
+ };
+
+ this.getSequence = function () {
+ return this.sequence;
+ };
+ }
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Sequence/Directives/SequenceEditDirective.js b/Resources/public/js/sequence/Sequence/Directives/SequenceEditDirective.js
new file mode 100644
index 000000000..4eadf7e75
--- /dev/null
+++ b/Resources/public/js/sequence/Sequence/Directives/SequenceEditDirective.js
@@ -0,0 +1,30 @@
+/**
+ * Activity Form directive
+ * Directive Documentation : https://docs.angularjs.org/guide/directive
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Sequence').directive('sequenceEdit', [
+ function () {
+ return {
+ restrict: 'E',
+ replace: true,
+ controller: 'SequenceEditCtrl',
+ controllerAs: 'sequenceEditCtrl',
+ templateUrl: AngularApp.webDir + 'bundles/ujmexo/js/sequence/Sequence/Partials/sequence.edit.html',
+ scope: {
+ sequence: '='
+ },
+ link: function (scope, element, attr, sequenceEditCtrl) {
+ // set current page to first page
+ console.log('sequence directive link method called');
+ sequenceEditCtrl.setSequence(scope.sequence);
+
+ }
+ };
+ }
+ ]);
+})();
+
+
diff --git a/Resources/public/js/sequence/Sequence/Partials/sequence.edit.html b/Resources/public/js/sequence/Sequence/Partials/sequence.edit.html
new file mode 100644
index 000000000..fc4960f0d
--- /dev/null
+++ b/Resources/public/js/sequence/Sequence/Partials/sequence.edit.html
@@ -0,0 +1,38 @@
+
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Sequence/Services/SequenceService.js b/Resources/public/js/sequence/Sequence/Services/SequenceService.js
new file mode 100644
index 000000000..cf60e7493
--- /dev/null
+++ b/Resources/public/js/sequence/Sequence/Services/SequenceService.js
@@ -0,0 +1,57 @@
+/**
+ * Exercise player service
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Sequence').factory('SequenceService', [
+ '$http',
+ '$filter',
+ '$q',
+ function PlayerService($http, $filter, $q) {
+
+
+ return {
+
+ /**
+ * Update the sequence
+ * @param sequence
+ * @returns
+ */
+ update : function (sequence){
+ var deferred = $q.defer();
+ // sequence constructor
+ function Sequence(sequence){
+ var ujm_sequence = {
+ name: sequence.name,
+ description: sequence.description,
+ startDate: new Date(sequence.startDate),
+ endDate: new Date(sequence.endDate)
+ };
+
+ return ujm_sequence;
+ }
+
+ var updated = new Sequence(sequence);
+
+ $http
+ .put(
+ Routing.generate('ujm_sequence_update', { id : sequence.id }),
+ {
+ sequence_type: updated
+ }
+ )
+ .success(function (response){
+ deferred.resolve(response);
+ })
+ .error(function(data, status){
+ console.log('sequence service, update method error');
+ console.log(status);
+ console.log(data);
+ });
+ return deferred;
+ }
+ };
+ }
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Sequence/module.js b/Resources/public/js/sequence/Sequence/module.js
new file mode 100644
index 000000000..7ed3e012e
--- /dev/null
+++ b/Resources/public/js/sequence/Sequence/module.js
@@ -0,0 +1,11 @@
+/**
+ * Page module
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Sequence', [
+
+ ]);
+})();
+
diff --git a/Resources/public/js/sequence/Step/Controllers/StepEditCtrl.js b/Resources/public/js/sequence/Step/Controllers/StepEditCtrl.js
new file mode 100644
index 000000000..6074a9c6b
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Controllers/StepEditCtrl.js
@@ -0,0 +1,138 @@
+(function () {
+ 'use strict';
+
+ angular.module('Step').controller('StepEditCtrl', [
+ 'StepService',
+ function (StepService) {
+
+ this.steps = {};
+ this.currentStepIndex = 0;
+
+ // options for sortable steps
+ this.sortableOptions = {
+ placeholder: "placeholder",
+ axis: 'x',
+ stop: function (e, ui) {
+ this.updateStepsOrder();
+ }.bind(this),
+ cancel: ".unsortable",
+ items: "li:not(.unsortable)"
+ };
+
+ // Tiny MCE options
+ this.tinymceOptions = {
+ relative_urls: false,
+ theme: 'modern',
+ browser_spellcheck: true,
+ autoresize_min_height: 100,
+ autoresize_max_height: 500,
+ plugins: [
+ 'autoresize advlist autolink lists link image charmap print preview hr anchor pagebreak',
+ 'searchreplace wordcount visualblocks visualchars fullscreen',
+ 'insertdatetime media nonbreaking save table directionality',
+ 'template paste textcolor emoticons code'
+ ],
+ toolbar1: 'undo redo | styleselect | bold italic underline | forecolor | alignleft aligncenter alignright | preview fullscreen',
+ paste_preprocess: function (plugin, args) {
+ var link = $('' + args.content + '
').text().trim(); //inside div because a bug of jquery
+ var url = link.match(/^(((ftp|https?):\/\/)[\-\w@:%_\+.~#?,&\/\/=]+)|((mailto:)?[_.\w-]+@([\w][\w\-]+\.)+[a-zA-Z]{2,3})$/);
+
+ if (url) {
+ args.content = '' + link + '';
+ window.Claroline.Home.generatedContent(link, function (data) {
+ insertContent(data);
+ }, false);
+ }
+ }
+ };
+
+ // Step constructor
+ var my = this;
+ var Step = function () {
+ var ujm_step = {
+ description: 'New step default description
',
+ position: my.steps.length,
+ shuffle: false,
+ sequenceId: my.steps[0].sequenceId,
+ isLast: false,
+ isFirst: false
+ };
+ return ujm_step;
+ };
+
+ this.addStep = function () {
+ // create a new step
+ var step = new Step();
+ // update last step position
+ var last = this.steps[this.steps.length - 1];
+ last.position = step.position + 1;
+ // add new step at the right index in steps array
+ this.steps.splice(this.steps.length - 1, 0, step);
+ };
+
+ this.removeStep = function () {
+ var current = this.steps[this.currentStepIndex];
+ if (current && !current.isLast && !current.isFirst) {
+ var index = this.steps.indexOf(current);
+ // update positions...
+ for (var i = index; i < this.steps.length; i++) {
+ var step = this.steps[i];
+ step.position = step.position - 1;
+ }
+ // remove step
+ this.steps.splice(index, 1);
+ }
+ };
+
+ this.update = function () {
+ var promise = StepService.update(this.steps);
+ promise.then(function (result) {
+ console.log('steps update success');
+ }, function (error) {
+ console.log('steps update error');
+ });
+
+ };
+
+ this.getNextStep = function () {
+ var newIndex = this.currentStepIndex + 1;
+ if (this.steps[newIndex]) {
+ this.currentStepIndex = newIndex;
+ } else {
+ this.currentStepIndex = 0;
+ }
+ };
+
+ this.getPreviousStep = function () {
+ var newIndex = this.currentStepIndex - 1;
+ if (this.steps[newIndex]) {
+ this.currentStepIndex = newIndex;
+ } else {
+ this.currentStepIndex = this.steps.length - 1;
+ }
+ };
+
+ // on dragg end
+ this.updateStepsOrder = function(){
+ var index = 0;
+ for(index; index < this.steps.length; index++){
+ var step = this.steps[index];
+ step.position = index + 1;
+ }
+ };
+
+ this.setSteps = function (steps) {
+ this.steps = steps;
+ };
+
+ this.getSteps = function () {
+ return this.steps;
+ };
+
+ this.setCurrentStep = function (step) {
+ var index = this.steps.indexOf(step);
+ this.currentStepIndex = index;
+ };
+ }
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Step/Controllers/StepShowCtrl.js b/Resources/public/js/sequence/Step/Controllers/StepShowCtrl.js
new file mode 100644
index 000000000..d61a45572
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Controllers/StepShowCtrl.js
@@ -0,0 +1,14 @@
+(function () {
+ 'use strict';
+
+ angular.module('Step').controller('StepShowCtrl', [
+ 'StepService',
+ function (StepService) {
+
+
+ this.sayHello = function (name) {
+ console.log(StepService.hello(name));
+ };
+ }
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Step/Directives/StepEditDirective.js b/Resources/public/js/sequence/Step/Directives/StepEditDirective.js
new file mode 100644
index 000000000..fd2799fc8
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Directives/StepEditDirective.js
@@ -0,0 +1,27 @@
+/**
+ * Activity Form directive
+ * Directive Documentation : https://docs.angularjs.org/guide/directive
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Step').directive('stepEdit', [
+ function () {
+ return {
+ restrict: 'E',
+ replace: true,
+ controller: 'StepEditCtrl',
+ controllerAs: 'stepEditCtrl',
+ templateUrl: AngularApp.webDir + 'bundles/ujmexo/js/sequence/Step/Partials/step.edit.html',
+ scope: {
+ steps: '='
+ },
+ link: function (scope, element, attr, stepEditCtrl) {
+ // set current page to first page
+ console.log('step edit directive link method called');
+ stepEditCtrl.setSteps(scope.steps);
+ }
+ };
+ }
+ ]);
+})();
diff --git a/Resources/public/js/sequence/Step/Directives/StepShowDirective.js b/Resources/public/js/sequence/Step/Directives/StepShowDirective.js
new file mode 100644
index 000000000..f41271f68
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Directives/StepShowDirective.js
@@ -0,0 +1,24 @@
+
+(function () {
+ 'use strict';
+
+ angular.module('Step').directive('stepShow', [
+ function () {
+ return {
+ restrict: 'E',
+ replace: true,
+ controller: 'StepShowCtrl',
+ controllerAs: 'stepShowCtrl',
+ templateUrl: AngularApp.webDir + 'bundles/ujmexo/js/sequence/Step/Partials/step.show.html',
+ scope: {
+ steps: '='
+ },
+ link: function (scope, element, attr, stepShowCtrl) {
+
+ }
+ };
+ }
+ ]);
+})();
+
+
diff --git a/Resources/public/js/sequence/Step/Partials/step.edit.html b/Resources/public/js/sequence/Step/Partials/step.edit.html
new file mode 100644
index 000000000..e690cce99
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Partials/step.edit.html
@@ -0,0 +1,46 @@
+
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Step/Partials/step.show.html b/Resources/public/js/sequence/Step/Partials/step.show.html
new file mode 100644
index 000000000..edfb3a15c
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Partials/step.show.html
@@ -0,0 +1,11 @@
+
+
+
This is the "show exercise player" partial panel header
+
+
+
This is the "show exercise player" partial panel body
+
+ Nothing is implemented in the player yet :(
+ Please use the administrate resource menu to see the actual exercise player interface in edit mode.
+
+
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Step/Services/StepService.js b/Resources/public/js/sequence/Step/Services/StepService.js
new file mode 100644
index 000000000..fc913c672
--- /dev/null
+++ b/Resources/public/js/sequence/Step/Services/StepService.js
@@ -0,0 +1,41 @@
+/**
+ * Page Service
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Step').factory('StepService', [
+ '$http',
+ '$filter',
+ '$q',
+ function StepService($http, $filter, $q) {
+
+ return {
+
+ /**
+ * Update exercise player steps
+ * @param player
+ * @returns
+ */
+ update : function (steps){
+ var deferred = $q.defer();
+ var sequenceId = steps[0].sequenceId;
+ $http
+ .post(
+ Routing.generate('ujm_steps_update', { id : sequenceId}), {steps: steps}
+ )
+ .success(function (response){
+ deferred.resolve(response);
+ })
+ .error(function(data, status){
+ console.log('Step service, update method error');
+ console.log(status);
+ console.log(data);
+ });
+
+ return deferred.promise;
+ }
+ };
+ }
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/Step/module.js b/Resources/public/js/sequence/Step/module.js
new file mode 100644
index 000000000..d5a8a5fc9
--- /dev/null
+++ b/Resources/public/js/sequence/Step/module.js
@@ -0,0 +1,11 @@
+/**
+ * Page module
+ */
+(function () {
+ 'use strict';
+
+ angular.module('Step', [
+
+ ]);
+})();
+
diff --git a/Resources/public/js/sequence/editor.module.js b/Resources/public/js/sequence/editor.module.js
new file mode 100644
index 000000000..cc2fb3218
--- /dev/null
+++ b/Resources/public/js/sequence/editor.module.js
@@ -0,0 +1,15 @@
+(function () {
+ 'use strict';
+
+ // exercise player module
+ angular.module('SequenceEditApp', [
+ 'ngSanitize',
+ 'ui.bootstrap',
+ 'ui.sortable',
+ 'ui.tinymce',
+ 'ui.translation',
+ 'ui.resourcePicker',
+ 'Step',
+ 'Sequence'
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/js/sequence/player.module.js b/Resources/public/js/sequence/player.module.js
new file mode 100644
index 000000000..8a6f19222
--- /dev/null
+++ b/Resources/public/js/sequence/player.module.js
@@ -0,0 +1,13 @@
+(function () {
+ 'use strict';
+
+ // exercise player module
+ angular.module('SequenceViewApp', [
+ 'ngSanitize',
+ 'ui.bootstrap',
+ 'ui.tinymce',
+ 'ui.translation',
+ 'ui.resourcePicker',
+ 'Step'
+ ]);
+})();
\ No newline at end of file
diff --git a/Resources/public/less/ujm-sequence.less b/Resources/public/less/ujm-sequence.less
new file mode 100644
index 000000000..bd8b85c4c
--- /dev/null
+++ b/Resources/public/less/ujm-sequence.less
@@ -0,0 +1,45 @@
+.ujm-sequence-container{
+ width: 100%;
+ background: white;
+ padding: 15px;
+ .player-container{
+
+ }
+ .step-container{
+
+ ul.step-list{
+ list-style-type: none;
+ li.step-list-item {
+ display: inline-block;
+ line-height: 50px;
+ width:50px;
+ height: 50px;
+ vertical-align: middle;
+ border: 1px solid black;
+ cursor: pointer;
+ background-color: rgba(255, 255, 255, 0.8);
+ margin: 5px;
+ border-radius: 5px;
+ div {
+ display: inline-block;
+ /*padding-top: 12px;*/
+ }
+
+ }
+ li.step-list-item.active{
+ background-color: rgba(66, 139, 202, 1);
+ }
+ .placeholder {
+ display: inline-block;
+ border: 1px dotted #aaa;
+ width:35px;
+ line-height: 50px;
+ height: 35px;
+ /*padding-top:10px;*/
+ vertical-align: middle;
+ border-radius: 5px;
+ background-color: rgba(66, 139, 202, 0.2);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Resources/translations/resource.en.yml b/Resources/translations/resource.en.yml
index f5741f66d..b6876fa64 100755
--- a/Resources/translations/resource.en.yml
+++ b/Resources/translations/resource.en.yml
@@ -1 +1,4 @@
+# u
ujm_exercise: Exercise
+ujm_sequence: UJM Sequence
+ujm_sequence_administrate: Administrate sequence
diff --git a/Resources/translations/resource.fr.yml b/Resources/translations/resource.fr.yml
index 384d591f0..8211f11c3 100755
--- a/Resources/translations/resource.fr.yml
+++ b/Resources/translations/resource.fr.yml
@@ -1 +1,4 @@
+# u
ujm_exercise: Exercice
+ujm_sequence: Séquence UJM
+ujm_sequence_administrate: Administrer la séquence
diff --git a/Resources/translations/ujm_sequence.en.yml b/Resources/translations/ujm_sequence.en.yml
new file mode 100644
index 000000000..9f95ccde8
--- /dev/null
+++ b/Resources/translations/ujm_sequence.en.yml
@@ -0,0 +1,19 @@
+# e
+sequence_panel_title: Sequence
+sequence_panel_hide: Hide / Show sequence informations
+sequence_type_name : Sequence name
+sequence_type_description : Sequence description
+sequence_start_date: Start date
+sequence_end_date: End date
+sequence_name : Name
+sequence_description : Description
+sequence_step_description: Description
+sequence_step_shuffle: Shuffle
+sequence_step_previous: Previous step
+sequence_step_next: Next step
+sequence_step_view: View step
+sequence_steps_save: Save steps
+sequence_step_add: Add step
+sequence_step_remove: Remove step
+sequence_save: Save sequence
+sequence_step_panel_title: Steps
diff --git a/Resources/translations/ujm_sequence.fr.yml b/Resources/translations/ujm_sequence.fr.yml
new file mode 100644
index 000000000..76c070eed
--- /dev/null
+++ b/Resources/translations/ujm_sequence.fr.yml
@@ -0,0 +1,19 @@
+# e
+sequence_panel_title: Séquence
+sequence_panel_hide: Cacher / Montrer les informations de la séquence
+sequence_type_name : Nom de la séquence
+sequence_type_description : Description de la séquence
+sequence_start_date: Date de début
+sequence_end_date: Date de fin
+sequence_name : Nom
+sequence_description : Description
+sequence_step_description: Description
+sequence_step_shuffle: Aléatoire
+sequence_step_previous: Etape précédente
+sequence_step_next: Etape suivante
+sequence_step_view: Voir l'étape
+sequence_steps_save: Sauvegarder les étapes
+sequence_step_add: Ajouter une étape
+sequence_step_remove: Supprimer une étape
+sequence_save: Sauvegarder la séquence
+sequence_step_panel_title: Etapes
\ No newline at end of file
diff --git a/Resources/views/Sequence/edit.html.twig b/Resources/views/Sequence/edit.html.twig
new file mode 100644
index 000000000..8c3db68d9
--- /dev/null
+++ b/Resources/views/Sequence/edit.html.twig
@@ -0,0 +1,39 @@
+{% extends "UJMExoBundle:Sequence:layout.html.twig" %}
+
+{% block breadcrumb %}
+ {{ parent() }}
+{% endblock %}
+
+{% block content %}
+
+
+
+
+{% endblock %}
+
+
+{% block javascripts %}
+ {# Load Claroline JS #}
+ {{ parent() }}
+
+ {% javascripts debug=false filter='jsmin' output='vendor/ujmexo/ujm_sequence_edit.js'
+
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Controllers/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Directives/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Services/*'
+
+ '@UJMExoBundle/Resources/public/js/sequence/Step/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Step/Controllers/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Step/Directives/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Step/Services/*'
+
+ '@UJMExoBundle/Resources/public/js/sequence/editor.module.js'
+ %}
+
+ {% endjavascripts %}
+
+
+{% endblock %}
+
+
diff --git a/Resources/views/Sequence/layout.html.twig b/Resources/views/Sequence/layout.html.twig
new file mode 100644
index 000000000..a26ebf482
--- /dev/null
+++ b/Resources/views/Sequence/layout.html.twig
@@ -0,0 +1,60 @@
+{% extends "ClarolineCoreBundle:Workspace:layout.html.twig" %}
+
+{% block stylesheets %}
+ {# Claroline CSS #}
+ {{ parent() }}
+
+ {# Exercise Player styles #}
+ {% stylesheets debug=false filter='lessphp, cssmin' output='bundles/ujmexo/css/ujm-sequence.css'
+ '@UJMExoBundle/Resources/public/less/ujm-sequence.less'
+ %}
+
+ {% endstylesheets %}
+{% endblock %}
+
+{% block content %}
+
+{% endblock %}
+
+{% block javascripts %}
+ {# Claroline JS #}
+ {{ parent() }}
+
+ {# Translations #}
+
+
+ {# Angular JS #}
+ {% javascripts debug=false filter='jsmin' output='vendor/ujmexo/ujm-exo-angular-js.js'
+ '@InnovaAngularJSBundle/Resources/public/js/angular.min.js'
+ '@InnovaAngularJSBundle/Resources/public/js/angular-sanitize.min.js'
+ '@InnovaAngularJSBundle/Resources/public/js/angular-route.min.js'
+ '@InnovaAngularJSBundle/Resources/public/js/angular-resource.min.js'
+ '@InnovaAngularJSBundle/Resources/public/js/angular-touch.min.js'
+ %}
+
+ {% endjavascripts %}
+
+ {# Angular UI #}
+ {% javascripts debug=false filter='jsmin' output='vendor/ujmexo/ujm-exo-angular-ui.js'
+ '@InnovaAngularUIBootstrapBundle/Resources/public/js/*'
+ '@InnovaAngularUITinyMCEBundle/Resources/public/js/*'
+ '@InnovaAngularUITranslationBundle/Resources/public/js/*'
+ '@InnovaAngularUIResourcePickerBundle/Resources/public/js/*'
+ '@InnovaAngularUISortableBundle/Resources/public/js/*'
+ %}
+
+ {% endjavascripts %}
+
+
+
+ {# Set some vars needed by Angular parts #}
+
+{% endblock %}
diff --git a/Resources/views/Sequence/view.html.twig b/Resources/views/Sequence/view.html.twig
new file mode 100644
index 000000000..843586b0d
--- /dev/null
+++ b/Resources/views/Sequence/view.html.twig
@@ -0,0 +1,35 @@
+{% extends "UJMExoBundle:Sequence:layout.html.twig" %}
+
+
+
+{% block breadcrumb %}
+ {{ parent() }}
+{% endblock %}
+
+{% block content %}
+
+
+
+{% endblock %}
+
+
+{% block javascripts %}
+ {# Load Claroline JS #}
+ {{ parent() }}
+
+
+
+ {% javascripts debug=false filter='jsmin' output='vendor/ujmexo/ujm_sequence_show.js'
+
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Controllers/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Directives/*'
+ '@UJMExoBundle/Resources/public/js/sequence/Sequence/Services/*'
+
+ '@UJMExoBundle/Resources/public/js/player/player.module.js'
+ %}
+
+ {% endjavascripts %}
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/UJMExoBundle.php b/UJMExoBundle.php
index 6d13090d2..a4154c24c 100755
--- a/UJMExoBundle.php
+++ b/UJMExoBundle.php
@@ -5,23 +5,38 @@
use Claroline\CoreBundle\Library\PluginBundle;
use Claroline\KernelBundle\Bundle\ConfigurationBuilder;
-class UJMExoBundle extends PluginBundle
-{
+class UJMExoBundle extends PluginBundle {
- public function getContainerExtension()
- {
+ public function getContainerExtension() {
return new DependencyInjection\UJMExoExtension();
}
- public function getConfiguration($environment)
- {
+ public function getConfiguration($environment) {
$config = new ConfigurationBuilder();
return $config->addRoutingResource(__DIR__ . '/Resources/config/routing.yml', null, 'exercise');
}
- public function getRequiredFixturesDirectory($environment)
- {
+ public function getRequiredFixturesDirectory($environment) {
return 'DataFixtures';
}
+
+ public function suggestConfigurationFor(Bundle $bundle, $environment) {
+ $bundleClass = get_class($bundle);
+ $config = new ConfigurationBuilder();
+ $emptyConfigs = array(
+ 'Innova\AngularJSBundle\InnovaAngularJSBundle',
+ 'Innova\AngularUIBootstrapBundle\InnovaAngularUIBootstrapBundle',
+ 'Innova\AngularUITranslationBundle\InnovaAngularUITranslationBundle',
+ 'Innova\AngularUIResourcePickerBundle\InnovaAngularUIResourcePickerBundle',
+ 'Innova\AngularUITinyMCEBundle\InnovaAngularUITinyMCEBundle',
+ 'Innova\AngularUIPageslideBundle\InnovaAngularUIPageslideBundle',
+ 'Innova\AngularUISortableBundle\AngularUISortableBundle',
+ );
+ if (in_array($bundleClass, $emptyConfigs)) {
+ return $config;
+ }
+ return false;
+ }
+
}
diff --git a/composer.json b/composer.json
index 1c0f90886..ee927ec04 100755
--- a/composer.json
+++ b/composer.json
@@ -4,13 +4,17 @@
"type": "claroline-plugin",
"require": {
"claroline/core-bundle": "~5.0",
- "icap/badge-bundle": "~5.0"
+ "icap/badge-bundle": "~5.0",
+ "innova/angular-js-bundle": "~5.1",
+ "innova/angular-ui-bootstrap-bundle" : "~5.0",
+ "innova/angular-ui-tinymce-bundle" : "~5.0",
+ "innova/angular-ui-translation-bundle" : "~5.0",
+ "innova/angular-ui-resource-picker-bundle" : "~6.0",
+ "innova/angular-ui-sortable-bundle" : "~5.0",
+ "innova/angular-ui-pageslide-bundle" : "~5.0"
},
"autoload": {
"psr-0": { "UJM\\ExoBundle": "" }
},
- "target-dir": "UJM/ExoBundle",
- "extra": {
- "dbVersion": "20150416104535"
- }
+ "target-dir": "UJM/ExoBundle"
}