You are here

opigno_module.module in Opigno module 3.x

Same filename and directory in other branches
  1. 8 opigno_module.module

File

opigno_module.module
View source
<?php

/**
 * @file
 * Contains opigno_module.module.
 */
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Link;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\Core\File\FileSystemInterface;
use Drupal\group\Entity\Group;
use Drupal\h5p\H5PDrupal\H5PDrupal;
use Drupal\opigno_group_manager\OpignoGroupContext;
use Drupal\opigno_learning_path\LearningPathAccess;
use Drupal\opigno_module\Entity\OpignoModule;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\ViewExecutable;
use Drupal\opigno_module\Controller\ExternalPackageController;
use Drupal\Core\Access\AccessResultAllowed;
use Drupal\opigno_module\Entity\OpignoActivity;
use Drupal\opigno_learning_path\Entity\LPModuleAvailability;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Render\Element\StatusMessages;
use Drupal\Core\Ajax\SettingsCommand;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Ajax\RedirectCommand;
use Drupal\Core\Database\Query\Condition;
define('OPIGNO_MODULE_PPT_TEMP_DIR', 'external_packages_ppt');

/**
 * Implements hook_help().
 */
function opigno_module_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {

    // Main module help for the opigno_module module.
    case 'help.page.opigno_module':
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
      $output .= '<p>' . t('Opigno Modules functionality') . '</p>';
      return $output;
    default:
  }
}

/**
 * Implements hook_theme().
 */
function opigno_module_theme() {
  $theme = [];
  $theme['opigno_activity'] = [
    'render element' => 'elements',
    'file' => 'opigno_module.theme.inc',
    'template' => 'opigno_activity',
  ];
  $theme['opigno_activity_content_add_list'] = [
    'render element' => 'content',
    'variables' => [
      'content' => NULL,
    ],
    'file' => 'opigno_module.theme.inc',
  ];
  $theme['opigno_answer'] = [
    'render element' => 'elements',
    'file' => 'opigno_answer.page.inc',
    'template' => 'opigno_answer',
  ];
  $theme['opigno_answer_content_add_list'] = [
    'render element' => 'content',
    'variables' => [
      'content' => NULL,
    ],
    'file' => 'opigno_answer.page.inc',
  ];
  $theme['opigno_module_item_form'] = [
    'variables' => [],
  ];
  $theme['opigno_user_result_item'] = [
    'variables' => [
      'opigno_answer' => NULL,
      'opigno_answer_activity' => NULL,
      'question_number' => NULL,
      'answer_max_score' => NULL,
    ],
  ];
  $theme['opigno_module_activity_from'] = [
    'render element' => 'content',
  ];
  $theme['block__opigno_module_learning_path_progress_block'] = [
    'base hook' => 'block',
    'progress' => NULL,
    'fullpage' => TRUE,
    'home_link' => NULL,
    'ajax_conteiner' => FALSE,
  ];
  $theme['opigno_module'] = [
    'render element' => 'elements',
    'file' => 'opigno_module.theme.inc',
    'template' => 'opigno_module',
  ];
  return $theme;
}

/**
 * User result item preprocess template.
 */
function template_preprocess_opigno_user_result_item(&$variables) {
  $route = \Drupal::routeMatch();

  /** @var \Drupal\opigno_module\Entity\OpignoModule $opigno_module */
  $opigno_module = $route
    ->getParameter('opigno_module');

  /** @var \Drupal\opigno_module\Entity\OpignoAnswer $answer */
  $answer = $variables['opigno_answer'];

  // Check if module allow display answer content for users.
  $hide_results = $opigno_module
    ->getHideResults($answer);

  /** @var Drupal\opigno_module\Entity\OpignoActivity $answer_activity */
  $answer_activity = $variables['opigno_answer_activity'];

  /* @var $answer_service \Drupal\opigno_module\ActivityAnswerManager */
  $answer_service = \Drupal::service('plugin.manager.activity_answer');
  $answer_activity_type = $answer_activity
    ->getType();
  if (!$hide_results) {

    // Get the data about an answer.
    if ($answer_service
      ->hasDefinition($answer_activity_type)) {
      $answer_instance = $answer_service
        ->createInstance($answer_activity_type);
      $headings = $answer_instance
        ->getAnswerResultItemHeaders($answer);
      $result_data = $answer_instance
        ->getAnswerResultItemData($answer);
      if (!empty($result_data)) {
        $content = [
          '#theme' => 'table',
          '#header' => $headings,
          '#rows' => $result_data,
        ];
        $variables['content'] = $content;
      }
    }

    // Output question body field.
    if ($answer_activity
      ->hasField('opigno_body')) {
      $viewBuilder = \Drupal::entityTypeManager()
        ->getViewBuilder('opigno_activity');
      $variables['question_body'] = $viewBuilder
        ->viewField($answer_activity
        ->get('opigno_body'), 'full');
    }
  }

  // Output question number info.
  if ($variables['question_number']) {
    $activity_title = $answer_activity
      ->getName();
    $variables['label'] = $variables['question_number'] . '. ' . ucfirst($activity_title);
  }
  if (isset($variables['answer_max_score']) && $variables['answer_max_score'] > 0) {

    // Get the user score.
    $variables['score'] = t('Score: %score of %max_score', [
      '%score' => $answer
        ->isEvaluated() ? $answer
        ->getScore() : '?',
      '%max_score' => $variables['answer_max_score'],
    ]);
  }
  else {
    $variables['score'] = t('No score retrieved');
  }
  $variables['#attached']['library'][] = 'opigno_module/module_results_page';
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function opigno_module_theme_suggestions_opigno_activity(array $variables) {
  $suggestions = [];
  $entity = $variables['elements']['#opigno_activity'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  $suggestions[] = 'opigno_activity__' . $sanitized_view_mode;
  $suggestions[] = 'opigno_activity__' . $entity
    ->bundle();
  $suggestions[] = 'opigno_activity__' . $entity
    ->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'opigno_activity__' . $entity
    ->id();
  $suggestions[] = 'opigno_activity__' . $entity
    ->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_theme_suggestions_HOOK().
 */
function opigno_module_theme_suggestions_opigno_answer(array $variables) {
  $suggestions = [];
  $entity = $variables['elements']['#opigno_answer'];
  $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_');
  $suggestions[] = 'opigno_answer__' . $sanitized_view_mode;
  $suggestions[] = 'opigno_answer__' . $entity
    ->bundle();
  $suggestions[] = 'opigno_answer__' . $entity
    ->bundle() . '__' . $sanitized_view_mode;
  $suggestions[] = 'opigno_answer__' . $entity
    ->id();
  $suggestions[] = 'opigno_answer__' . $entity
    ->id() . '__' . $sanitized_view_mode;
  return $suggestions;
}

/**
 * Implements hook_theme_suggestions_alter().
 */
function opigno_module_theme_suggestions_alter(array &$suggestions, array $variables, $hook) {
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  if (($route_name == 'opigno_module.manager.get_item_form' || $route_name == 'opigno_module.add_external_package' || $route_name == 'opigno_module.add_external_package_ppt') && $hook == 'page') {
    $suggestions[] = 'opigno_module_item_form';
  }
  if (opigno_module_is_activity_route()) {
    if ($hook == 'region' && in_array('region__content', $suggestions)) {
      $suggestions[] = 'region__content__opigno_module__activity';
    }
    if ($hook == 'page') {
      $suggestions[] = 'page__opigno_module__activity';
    }
  }
}

/**
 * Implements hook_preprocess_page().
 */
function opigno_module_preprocess_html(&$variables) {

  // Remove admin bar for item form (included in iframe)
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  if ($route_name == 'opigno_module.manager.get_item_form' || $route_name == 'opigno_module.add_external_package' || $route_name == 'opigno_module.add_external_package_ppt') {
    unset($variables['page_top']);
  }
  if ($route_name == 'opigno_module.group.answer_form') {
    $variables['attributes']['class'][] = 'section-module';
    if (!empty(\Drupal::request()->cookies
      ->get('fullscreen'))) {
      $variables['attributes']['class'][] = 'fullscreen';
    }
  }
}

/**
 * Implements hook_form_alter().
 */
function opigno_module_form_alter(&$form, FormStateInterface &$form_state, $form_id) {
  $build_info = $form_state
    ->getBuildInfo();
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  $moduleHandler = \Drupal::service('module_handler');

  // Alter taxonomy term form for vocabulary 'Skills'.
  if ($moduleHandler
    ->moduleExists('opigno_skills_system') && $form_id == 'taxonomy_term_skills_form' && method_exists($build_info['callback_object'], 'getEntity')) {
    $entity = $build_info['callback_object']
      ->getEntity();
    $form['relations']['#open'] = TRUE;
    $form['relations']['#type'] = 'fieldset';
    $form['relations']['parent']['#multiple'] = FALSE;
    $form['relations']['#title'] = t('Choose parent tree or parent skill');
    $form['relations']['#weight'] = 2;
    unset($form['relations']['parent']['#title']);
    unset($form["relations"]["weight"]);
    $form['field_minimum_score']['widget'][0]['value']['#default_value'] = 100;
    $form['field_minimum_count_of_answers']['widget'][0]['value']['#default_value'] = 2;

    // Do custom validation first.
    array_unshift($form['#validate'], '_skills_term_validation');

    // Check if it is a target skill.
    $target_skill = $entity
      ->get('parent')
      ->getValue()[0]['target_id'] == 0 ? TRUE : FALSE;
    if ($entity
      ->isNew()) {
      $description = t('Check this option if you are creating a new skill tree, with a target skill on top of it.
          You will then be able to add several skills to this tree and organize them with a hierarchy.
          This target skill can be used in modules using skill system,
          so that the module will check that users master all the skills in the related skill tree.
          But you won’t be able to assign this skill to activities (only activities that are not target skills can be assigned to activities).');
      $form['target_skill'] = [
        '#title' => t('This is target skill'),
        '#description' => $description,
        '#type' => 'checkbox',
        '#default_value' => 0,
      ];
    }
    if ($target_skill && !$entity
      ->isNew()) {

      // Hide fields with minimum score and minimum count of answers for target skill.
      if (isset($form['field_minimum_count_of_answers'])) {
        $form['field_minimum_count_of_answers']['#access'] = FALSE;
      }
      if (isset($form['field_minimum_score'])) {
        $form['field_minimum_score']['#access'] = FALSE;
      }
      if (isset($form['field_level_names'])) {
        $form['field_level_names']['#access'] = FALSE;
      }
      $form['target_skill'] = [
        '#title' => t('This is target skill'),
        '#type' => 'checkbox',
        '#default_value' => 1,
        '#disabled' => TRUE,
      ];
    }
    else {
      $values = $form_state
        ->getValues();
      $ajax_wrapper = 'skill-level-names';
      unset($form['field_level_names']['widget']['add_more']);
      $form['field_level_names']['#prefix'] = '<div id="skill-level-names">';
      $form['field_level_names']['#suffix'] = '</div>';
      $form['field_minimum_score']['#states'] = [
        'visible' => [
          ':input[name="target_skill"]' => [
            'checked' => FALSE,
          ],
        ],
      ];
      $form['field_level_names']['#states'] = [
        'visible' => [
          ':input[name="target_skill"]' => [
            'checked' => FALSE,
          ],
        ],
      ];
      $form['field_minimum_count_of_answers']['#states'] = [
        'visible' => [
          ':input[name="target_skill"]' => [
            'checked' => FALSE,
          ],
        ],
      ];
      $form['relations']['#states'] = [
        'visible' => [
          ':input[name="target_skill"]' => [
            'checked' => FALSE,
          ],
        ],
      ];
      if (!empty($values)) {
        $max_delta = $values['count_of_levels'] - 1;
        $default_option = $form['field_level_names']['widget'][0];
        for ($i = 0; $i <= $max_delta; $i++) {
          if (empty($values['field_level_names'][$i])) {
            $form['field_level_names']['widget'][$i] = $default_option;
            $form['field_level_names']['widget'][$i]['value']['#default_value'] = 'Level ' . ($i + 1);
            $form['field_level_names']['widget'][$i]['#delta'] = $i;
            $form['field_level_names']['widget'][$i]['#weight'] = $i;
            $form['field_level_names']['widget'][$i]['_weight']['#default_value'] = $i;
          }
        }
        foreach ($form['field_level_names']['widget'] as $key => $option) {
          if (is_numeric($key) && $key > $max_delta) {
            unset($form['field_level_names']['widget'][$key]);
          }
        }
      }
      else {
        $max_delta = $form['field_level_names']['widget']['#max_delta'];
        if ($max_delta == 0) {
          $form['field_level_names']['widget'][0]['value']['#default_value'] = 'Level 1';
        }
        else {
          unset($form['field_level_names']['widget'][$max_delta]);
        }
      }
      $options = [];
      for ($i = 1; $i <= 10; $i++) {
        $options[$i] = $i;
      }
      $form['count_of_levels'] = [
        '#type' => 'select',
        '#title' => t('Number of levels for this skill'),
        '#default_value' => $max_delta,
        '#required' => TRUE,
        '#options' => $options,
        '#weight' => 3,
        '#ajax' => [
          'event' => 'change',
          'callback' => '_skill_level_names_ajax',
          'wrapper' => $ajax_wrapper,
        ],
        '#states' => [
          'visible' => [
            ':input[name="target_skill"]' => [
              'checked' => FALSE,
            ],
          ],
        ],
      ];
      $form['field_level_names']['widget']['#prefix'] = '<div class="skill-levels-description">' . t('You can adapt the names of levels, listed from the highest to the lowest') . '</div>';
      $form['#attached']['library'][] = 'opigno_skills_system/opigno_skills_entity';
    }
  }
  if ($route_name == 'entity.opigno_activity.delete_form' && method_exists($build_info['callback_object'], 'getEntity')) {

    // Attach js with autosubmit.
    $form['#attached']['library'][] = 'opigno_module/activity';
    $entity = $build_info['callback_object']
      ->getEntity();
    $answers = $entity
      ->getAnswers();
    $modules = $entity
      ->getModules();
    $messages = [];
    if ($answers) {

      // Disallow activity deleting if it has users answers.
      $messages[] = t('There are some results for this activity, and it cannot consequently be deleted in order to keep the users\' achievements');
    }
    if (!empty($modules)) {
      $m_list = [];
      foreach ($modules as $module) {
        $m_list[] = $module
          ->getName();
      }

      // Disallow activity deleting if it has modules.
      $messages[] = \Drupal::translation()
        ->formatPlural(count($modules), 'This activity is linked to the following module: @modules. Please unlink first the activity from this module and try again to delete it', 'This activity is linked to the following modules: @modules. Please unlink first the activity from these modules and try again to delete it', [
        '@modules' => implode(', ', $m_list),
      ]);
    }
    if (!empty($messages)) {
      $form['message'] = [
        '#theme' => 'item_list',
        '#list_type' => 'ul',
        '#attributes' => [
          'class' => [
            'alert',
            'alert-danger',
          ],
        ],
        '#items' => $messages,
      ];
      $form['description'] = [];
      $form_state
        ->set('activity_answers', TRUE);
      $form_state
        ->set('activity_message', implode(', ', $messages));
      $form['actions']['submit']['#attributes']['disabled'] = 'disabled';
    }
  }

  // Change button value from "Save" to "Next".
  if (in_array($route_name, [
    'opigno_module.manager.get_item_form',
    'opigno_module.group.answer_form',
  ])) {
    $form['actions']['submit']['#value'] = t('Next');
  }

  // Improve module form display.
  if ($route_name == 'opigno_module.manager.get_item_form') {
    $form['uid']['#access'] = FALSE;
    $form['created']['#access'] = FALSE;
    if (isset($form['actions']['delete'])) {
      unset($form['actions']['delete']);
    }
  }

  // If the form has an entity, ajaxify it.
  if ($route_name == 'opigno_module.manager.get_item_form' && method_exists($build_info['callback_object'], 'getEntity')) {

    // Get the entity.

    /** @var \Drupal\Core\Entity\EntityInterface $entity */
    $entity = $build_info['callback_object']
      ->getEntity();
    $entity_type = $entity
      ->getEntityTypeId();
    $bundle = $entity
      ->bundle();
    $id = $entity
      ->id();

    // Add form class for ajaxification.
    // In case of add form, append "new" instead of the entity ID.
    $ajax_id = "ajax-form-entity-{$entity_type}-{$bundle}-" . ($id ?: 'new');
    $form['#attributes']['class'][] = $ajax_id;

    // Ajaxification settings of the buttons.
    $ajax_settings = [
      'callback' => 'Drupal\\opigno_module\\Controller\\OpignoModuleManagerController::ajaxFormEntityCallback',
      'wrapper' => $ajax_id,
      'effect' => 'fade',
      'event' => 'click',
    ];
    $form['#attached']['library'][] = 'opigno_module/ajax_form';
    $form['actions']['submit']['#ajax'] = $ajax_settings;
    $form['actions']['publish']['#ajax'] = $ajax_settings;
    $form['actions']['unpublish']['#ajax'] = $ajax_settings;
    $form['actions']['preview']['#access'] = FALSE;
    unset($form['actions']['publish']['#dropbutton']);
    unset($form['actions']['unpublish']['#dropbutton']);

    // Ajaxify the buttons.
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions'][$action]['#submit'][] = 'Drupal\\opigno_module\\Controller\\OpignoModuleManagerController::ajaxFormEntityFormSubmit';
      }
    }

    // Handle case of entity edition : define the options.
    if ($id) {
      $current_path = \Drupal::service('path.current')
        ->getPath();
      $path_args = explode('/', $current_path);
      if ($path_args[1] == 'ajax-form-entity') {

        // Case of edit link.
        $view_mode = $path_args[5];
        $reload = FALSE;
      }
      else {

        // Case of form inside the display of the entity.
        $view_mode = 'default';
        $reload = 'reload_entity';
      }
    }
    else {

      // Case of creation.
      $view_mode = 'default';
      $reload = TRUE;
    }

    // Add all configurations to the form to make it available everywhere.
    $form['ajax_form_entity'] = [
      '#type' => 'hidden',
      '#value' => [
        'view_mode' => $view_mode,
        'reload' => $reload,
        'content_selector' => ".{$ajax_id}",
        'form_selector' => ".{$ajax_id}",
      ],
    ];
  }
  if ($form_id == 'opigno_module_form') {
    $form['#attached']['library'][] = 'opigno_module/opigno_badges';

    // Make field "name" required.
    $form["name"]["widget"][0]["value"]["#required"] = TRUE;
  }
  if ($form_id == 'views_form_opigno_activities_bank_lp_interface_default') {

    // Hide duplicate button and unnecessary fields.
    $form['header']['#attributes']['hidden'] = 'hidden';

    // Rename submit button.
    $form['actions']['submit']['#value'] = t('Add selected activities to the module');
    $form['#attached']['library'][] = 'opigno_module/activities_bank';
    $route_name = \Drupal::routeMatch()
      ->getRouteName();
    if ($route_name == 'opigno_module.activities_bank_lpm') {
      $form['#attached']['drupalSettings']['activities_bank']['page'] = 'group';
      if (!empty($_SESSION['activities_bank']['checkboxes_ids'])) {
        $form['#attached']['drupalSettings']['activities_bank']['checkboxes_ids'] = $_SESSION['activities_bank']['checkboxes_ids'];
      }
      $form_state
        ->set('activities_bank_page', 'group');
      if (!empty($_COOKIE['activities_bank_activities'])) {
        unset($_COOKIE['activities_bank_activities']);
        setcookie('activities_bank_activities', '', time() - 3600);
      }
      if (!empty($_COOKIE['activities_bank_checked'])) {
        unset($_COOKIE['activities_bank_checked']);
        setcookie('activities_bank_checked', '', time() - 3600);
      }
    }
    elseif ($route_name == 'opigno_module.activities_bank') {
      $form['#attached']['drupalSettings']['activities_bank']['page'] = 'module';
    }
    $form['#submit'][] = 'opigno_module_activities_bank_lp_form_submit';
    $form["#validate"] = [
      'opigno_module_activities_bank_lp_form_validate',
    ];
  }
}

/**
 * Custom submit for view activities bank lp form.
 *
 * @param mixed $form
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 */
function opigno_module_activities_bank_lp_form_validate(&$form, FormStateInterface $form_state) {
  $form_state
    ->setValidationComplete();
}

/**
 * Ajaxify activity bank form.
 */
function opigno_module_form_views_form_opigno_activities_bank_lp_interface_default_alter(&$form, FormStateInterface &$form_state, $form_id) {
  $selector = 'ajaxify_submit_form_' . $form_id;
  $form['#prefix'] = '<div id="' . $selector . '">';
  $form['#suffix'] = '</div>';
  $form['actions']['submit']['#ajax'] = [
    'callback' => 'opigno_module_views_form_opigno_activities_bank_lp_interface_default_ajax_callback',
    'wrapper' => $selector,
    'effect' => 'fade',
  ];
}

/**
 * Implements opigno_module_views_form_opigno_activities_bank_lp_interface_default_ajax_callback().
 */
function opigno_module_views_form_opigno_activities_bank_lp_interface_default_ajax_callback($form, FormStateInterface &$form_state) {
  $form_id = $form['form_id']['#value'];
  $render_selector = 'ajaxify_submit_form_' . $form_id;

  // Used to display results of drupal_set_message() calls.
  $messages = StatusMessages::renderMessages(NULL);

  // Create AJAX Response object.
  $response = new AjaxResponse();
  $output = [];
  if ($form_state
    ->getErrors()) {
    $output[] = $messages;
    $response
      ->addCommand(new AppendCommand($render_selector, $output));
    $command = new SettingsCommand([
      'closeActivityBankPanel' => FALSE,
    ], TRUE);
  }
  else {

    // Default reload the page printing the status message.
    $output[] = $messages;
    $output[] = $form;
    $response
      ->addCommand(new HtmlCommand($render_selector, $output));
    $command = new SettingsCommand([
      'closeActivityBankPanel' => TRUE,
    ], TRUE);
  }
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  $opigno_module = \Drupal::routeMatch()
    ->getParameter('opigno_module');
  if ($opigno_module && $route_name == 'opigno_module.activities_bank') {
    $url = Url::fromRoute('opigno_module.modules', [
      'opigno_module' => $opigno_module
        ->id(),
    ]);
    $command = new RedirectCommand($url
      ->toString());
    $response
      ->addCommand($command);
  }
  $response
    ->addCommand($command);
  return $response;
}

/**
 * Ajax form submit.
 */
function _skill_level_names_ajax(array &$form, FormStateInterface $form_state) {
  return $form['field_level_names'];
}

/**
 * Custom validation on creating new skill term.
 */
function _skills_term_validation(array &$form, FormStateInterface $form_state) {
  $values = $form_state
    ->getValues();
  $values['parent'] = [
    $values['parent'] => $values['parent'],
  ];
  $form_state
    ->setValues($values);
  if (isset($values['parent'][0]) && (isset($values['target_skill']) && $values['target_skill'] == 0)) {
    $message = 'Please select parent tree or parent skill other than &lt;root&gt;, or check the option ‘This is target skill’ if you want to create a new skill tree.';
    $form_state
      ->setErrorByName('parent', t($message));
  }
}

/**
 * Custom submit for view activities bank lp form.
 *
 * @throws \Exception
 */
function opigno_module_activities_bank_lp_form_submit(&$form, FormStateInterface $form_state) {
  $activities = [];
  $storage = $form_state
    ->getStorage();

  // Is group edit page flag.
  $is_group_page = !empty($storage['activities_bank_page']) && $storage['activities_bank_page'] == 'group' ? TRUE : FALSE;
  if ($is_group_page && !empty($_SESSION['activities_bank']['activities_ids'])) {

    // Get activities ids if group edit page.
    $activities = $_SESSION['activities_bank']['activities_ids'];
  }
  if (!empty($_SESSION['activities_bank'])) {
    unset($_SESSION['activities_bank']);
  }
  if (!empty($_COOKIE['activities_bank_activities'])) {
    if (!$is_group_page) {

      // Get activities ids if module edit page.
      $activities = explode(',', $_COOKIE['activities_bank_activities']);
    }
    unset($_COOKIE['activities_bank_activities']);
    setcookie('activities_bank_activities', '', time() - 3600);
  }
  if (!empty($_COOKIE['activities_bank_checked'])) {
    unset($_COOKIE['activities_bank_checked']);
    setcookie('activities_bank_checked', '', time() - 3600);
  }
  if (!empty($activities)) {
    $activities = OpignoActivity::loadMultiple($activities);

    // Get URL parameters.
    $opigno_module = \Drupal::routeMatch()
      ->getParameter('opigno_module');
    if ($opigno_module) {

      /* @var \Drupal\opigno_module\Controller\OpignoModuleController $opigno_module_obj*/
      $opigno_module_obj = \Drupal::service('opigno_module.opigno_module');

      // Get activities that already added to module.
      $added_activities = $opigno_module
        ->getModuleActivities();
      $added_activities = array_map(function ($activity) {
        return $activity->id;
      }, $added_activities);

      // Filter activities that are added to a Module.
      $activities = array_filter($activities, function ($activity) use ($added_activities) {

        /* @var \Drupal\opigno_module\Entity\OpignoActivity $activity */
        return !in_array($activity
          ->id(), $added_activities);
      });

      // Add activities to a Module.
      if ($activities) {
        $save_acitivities = $opigno_module_obj
          ->activitiesToModule($activities, $opigno_module);
        \Drupal::messenger()
          ->addMessage(t('Added @num activities', [
          '@num' => count($activities),
        ]));
      }
    }
  }
}

/**
 * Helper function.
 *
 * Get Activities related to specific module.
 */
function opigno_module_get_activities($module_id, $module_vid = NULL) {

  /* @todo join table with activity revisions */
  $questions = [];

  /* @var $db_connection \Drupal\Core\Database\Connection */
  $db_connection = \Drupal::service('database');
  $query = $db_connection
    ->select('opigno_activity', 'oa');
  $query
    ->fields('oafd', [
    'id',
    'type',
    'name',
  ]);
  $query
    ->fields('omr', [
    'activity_status',
    'weight',
    'max_score',
    'auto_update_max_score',
    'omr_id',
    'omr_pid',
    'child_id',
    'child_vid',
  ]);
  $query
    ->addJoin('inner', 'opigno_activity_field_data', 'oafd', 'oa.id = oafd.id');
  $query
    ->addJoin('inner', 'opigno_module_relationship', 'omr', 'oa.id = omr.child_vid');
  $query
    ->condition('oafd.status', 1);
  $query
    ->condition('omr.parent_id', $module_id);
  if ($module_vid) {
    $query
      ->condition('omr.parent_vid', $module_vid);
  }
  $query
    ->condition('omr_pid', NULL, 'IS');
  $query
    ->orderBy('omr.weight');
  $result = $query
    ->execute();
  foreach ($result as $question) {
    $questions[] = $question;
  }
  return $questions;
}

/**
 * Implements hook_preprocess_page().
 */
function opigno_module_preprocess_page(&$variables) {
  if (opigno_module_is_activity_route()) {
    $user = \Drupal::currentUser();
    if ($gid = OpignoGroupContext::getCurrentGroupId()) {
      if ($group = \Drupal::entityTypeManager()
        ->getStorage('group')
        ->load($gid)) {
        $home_link = Link::createFromRoute(t('home'), 'entity.group.canonical', [
          'group' => $group
            ->id(),
        ], [
          'attributes' => [
            'class' => [
              'w-100',
            ],
          ],
        ])
          ->toRenderable();
        $variables['home_link'] = render($home_link);
      }
    }
    if ($user && isset($group)) {
      $progress_service = \Drupal::service('opigno_learning_path.progress');
      $variables['progress'] = $progress_service
        ->getProgressRound($group
        ->id(), $user
        ->id());
    }
    $variables['attributes']['class'][] = 'section-module';
  }
  $route_name = \Drupal::routeMatch()
    ->getRouteName();
  if (!empty($_SESSION['activities_bank']) && $route_name != 'opigno_module.activities_bank_lpm') {
    unset($_SESSION['activities_bank']);
  }
}

/**
 * Implements hook_page_attachments().
 */
function opigno_module_page_attachments(array &$page) {
  if (opigno_module_is_activity_route()) {
    $page['#attached']['library'][] = 'opigno_module/activity';
  }
  $page['#attached']['library'][] = 'opigno_module/menu_icon';
}

/**
 * Helper function.
 *
 * Is activity route.
 */
function opigno_module_is_activity_route() {
  return in_array(\Drupal::routeMatch()
    ->getRouteName(), [
    'opigno_module.group.answer_form',
  ]);
}

/**
 * Implements hook_h5p_styles_alter().
 */
function opigno_module_h5p_styles_alter(&$styles, $libraries, $mode) {
  if (isset($mode) && in_array($mode, [
    'iframe',
    'editor',
  ])) {
    if (!empty($libraries)) {
      foreach ($libraries as $library) {
        if (!is_array($library) && strpos($library, 'h5p/h5p.coursepresentation-') !== FALSE) {
          $styles[] = (object) [
            'path' => drupal_get_path('module', 'opigno_module') . '/css/h5p_course_presentation.css',
            'version' => '?ver=1.0.0',
          ];
          break;
        }
      }
    }
  }
}

/**
 * Run before import modules, activities etc to crete directories.
 */
function prepare_directory_structure_for_import() {
  $current_timestamp = \Drupal::time()
    ->getCurrentTime();
  $date = date('Y-m', $current_timestamp);
  $folders = [
    'public://opigno_scorm/',
    'public://opigno_tincan/',
    'public://' . $date . '/',
    'public://video-thumbnails/' . $date . '/',
  ];
  foreach ($folders as $folder) {
    \Drupal::service('file_system')
      ->prepareDirectory($folder, FileSystemInterface::MODIFY_PERMISSIONS | FileSystemInterface::CREATE_DIRECTORY);
  }
}

/**
 * Implements hook_page_top().
 */
function opigno_module_page_top(array &$page_top) {

  // Install h5p libraries.
  $interface = H5PDrupal::getInstance();
  if (empty($interface
    ->loadLibraries())) {
    $h5p_default_path = $interface
      ->getOption('default_path', 'h5p');
    $temporary_file_path = 'public://' . $h5p_default_path . '/temp/' . uniqid('h5p-');
    \Drupal::service('file_system')
      ->prepareDirectory($temporary_file_path, FileSystemInterface::MODIFY_PERMISSIONS | FileSystemInterface::CREATE_DIRECTORY);

    // Load libraries.h5p.
    $file_content = file_get_contents(drupal_get_path("profile", "opigno_lms") . "/h5plib/libraries.h5p");
    $file = file_save_data($file_content, $temporary_file_path . '.h5p', FileSystemInterface::EXISTS_REPLACE);
    if ($file) {
      $uri = $file
        ->getFileUri();
      $interface
        ->getUploadedH5pPath(\Drupal::service('file_system')
        ->realpath($uri));
      $interface
        ->getUploadedH5pFolderPath(\Drupal::service('file_system')
        ->realpath($temporary_file_path));

      // Validate libraries.
      $upgradeOnly = FALSE;
      $validator = H5PDrupal::getInstance('validator');
      $validator
        ->isValidPackage(TRUE, $upgradeOnly);

      // Save libraries.
      $storage = H5PDrupal::getInstance('storage');
      $storage
        ->savePackage(NULL, NULL, TRUE);
    }
  }
}

/**
 * Function for loading all users by role.
 */
function opigno_module_get_users_by_role($role) {
  if (!is_string($role)) {
    return FALSE;
  }
  $user_storage = \Drupal::service('entity_type.manager')
    ->getStorage('user');
  $ids = $user_storage
    ->getQuery()
    ->condition('status', 1)
    ->condition('roles', $role)
    ->execute();
  return $user_storage
    ->loadMultiple($ids);
}

/**
 * Implements hook_views_query_alter().
 */
function opigno_module_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

  // Filter not evaluated modules.
  if ($view
    ->id() === 'opigno_score_modules') {
    $user = \Drupal::currentUser();
    $roles = $user
      ->getRoles();
    if ($user
      ->hasPermission('evaluate modules')) {
      return;
    }
    if (in_array('administrator', $roles) || in_array('user_manager', $roles)) {

      // If current user is an admin and global user manager.
      return;
    }

    // Get trainings where the current user is a student manager.

    /** @var \Drupal\group\GroupMembershipLoaderInterface $membership_service */
    $membership_service = \Drupal::service('group.membership_loader');
    $memberships = $membership_service
      ->loadByUser($user, [
      'learning_path-user_manager',
      'opigno_class-class_manager',
    ]);
    $groups_ids = [];
    $members_ids = [];
    foreach ($memberships as $membership) {
      $group = $membership
        ->getGroup();
      $members = $group
        ->getMembers();
      foreach ($members as $member) {
        $members_ids[] = $member
          ->getUser()
          ->id();
      }
      if ($group
        ->bundle() == 'opigno_class') {
        $db_connection = \Drupal::service('database');
        $query_class = $db_connection
          ->select('group_content_field_data', 'g_c_f_d')
          ->fields('g_c_f_d', [
          'gid',
        ])
          ->condition('entity_id', $group
          ->id())
          ->condition('type', 'group_content_type_27efa0097d858')
          ->execute()
          ->fetchAll();
        foreach ($query_class as $result_ids) {
          $groups_ids[] = $result_ids->gid;
        }
      }
      else {
        $groups_ids[] = $group
          ->id();
      }
    }
    $members_ids = array_unique($members_ids);

    // Get modules.
    $modules_ids = [];
    if (!empty($groups_ids)) {
      foreach ($groups_ids as $id) {
        $group = Group::load($id);
        $mids = opigno_learning_path_get_modules_ids_by_group($group);
        $modules_ids = array_merge($modules_ids, $mids);
      }
    }

    // Filter listed modules by them.
    if (!empty($modules_ids)) {
      $query->where[] = [
        'conditions' => [
          [
            'field' => 'user_module_status.module',
            'value' => $modules_ids,
            'operator' => 'IN',
          ],
        ],
        'type' => 'AND',
      ];
      $query->where[] = [
        'conditions' => [
          [
            'field' => 'user_module_status.user_id',
            'value' => $members_ids,
            'operator' => 'IN',
          ],
        ],
        'type' => 'AND',
      ];
    }
    else {
      $query->where[] = [
        'conditions' => [
          [
            'field' => 'FALSE',
            'value' => [],
            'operator' => 'formula',
          ],
        ],
        'type' => 'AND',
      ];
    }
  }
  if ($view
    ->id() === 'opigno_activities_bank_lp_interface') {
    $route = \Drupal::routeMatch();
    $moduleHandler = \Drupal::service('module_handler');
    if (in_array($route
      ->getRouteName(), [
      'opigno_module.activities_bank',
      'opigno_module.activities_bank_lpm',
    ])) {

      /* @var \Drupal\opigno_module\Entity\OpignoModule $module */
      $module = $route
        ->getParameter('opigno_module');
      $activities = $module
        ->getModuleActivities();

      // Get all activities ids.
      $aids = array_keys($activities);

      // Filter activities to separate auto skills management and manual skills management.
      if ($moduleHandler
        ->moduleExists('opigno_skills_system')) {
        $activities_storage = \Drupal::entityTypeManager()
          ->getStorage('opigno_activity');
        $query_a = $activities_storage
          ->getQuery();
        if ($module
          ->getSkillsActive()) {
          if ($module
            ->getTargetSkill() && $module
            ->getTargetSkill() > 0) {
            $target_skill = $module
              ->getTargetSkill();
            $term_storage = \Drupal::entityTypeManager()
              ->getStorage('taxonomy_term');
            $skills_tree = $term_storage
              ->loadTree('skills', $target_skill);
            $skills_ids = [];
            foreach ($skills_tree as $skill) {
              $skills_ids[] = $skill->tid;
            }
            if (!empty($skills_ids)) {
              $group = $query_a
                ->orConditionGroup()
                ->condition('skills_list', $skills_ids, 'NOT IN')
                ->notExists('auto_skills');
              $ids = $query_a
                ->condition($group)
                ->execute();
              $aids = array_merge($aids, $ids);
            }
          }
        }
        else {
          $ids = $query_a
            ->condition('auto_skills', 1)
            ->execute();
          $aids = array_merge($aids, $ids);
        }
      }

      // Filter activities which are already added to module.
      if (!empty($aids)) {
        $query->where[] = [
          'conditions' => [
            [
              'field' => 'opigno_activity_field_data.id',
              'value' => $aids,
              'operator' => 'NOT IN',
            ],
          ],
          'type' => 'AND',
        ];
      }
      $condition = (new Condition('AND'))
        ->isNotNull('opigno_activity_field_data.name');
      $query
        ->addWhere(1, $condition);
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function opigno_module_form_system_file_system_settings_alter(&$form, FormStateInterface $form_state, $form_id) {
  $form['file_libreoffice_configs_path'] = [
    '#type' => 'textfield',
    '#title' => t('Libreoffice configs path'),
    '#description' => t('Libreoffice package used for converting media-files and require conversion configs directory.<br />
Depending to hosting necessary to create and set the location of Libreoffice conversion configs directory.<br />
Path starting with a slash located outside Drupal root directory.'),
    '#default_value' => ExternalPackageController::getLibreofficeConfigsDir(),
  ];
  $form['file_libreoffice_bin_path'] = [
    '#type' => 'textfield',
    '#title' => t('Libreoffice bin path'),
    '#description' => t('Enter the path to your libreoffice executable.'),
    '#default_value' => ExternalPackageController::getLibreOfficeBinPath(),
  ];
  $form['file_imagemagick_bin_path'] = [
    '#type' => 'textfield',
    '#title' => t('Imagemagick bin path'),
    '#description' => t('Enter the path to your imagemagick executable.'),
    '#default_value' => ExternalPackageController::getImagemagickBinPath(),
  ];
  $form['#submit'][] = 'opigno_module_form_system_file_system_settings_submit';
}

/**
 * Implements additional submit for the file system settings form.
 */
function opigno_module_form_system_file_system_settings_submit($form, FormStateInterface $form_state) {
  $config = \Drupal::configFactory()
    ->getEditable('opigno_module.settings');
  if ($form_state
    ->hasValue('file_libreoffice_configs_path')) {
    $config
      ->set('libreoffice_configs_path', $form_state
      ->getValue('file_libreoffice_configs_path'));
  }
  if ($form_state
    ->hasValue('file_libreoffice_bin_path')) {
    $config
      ->set('libreoffice_bin_path', $form_state
      ->getValue('file_libreoffice_bin_path'));
  }
  if ($form_state
    ->hasValue('file_imagemagick_bin_path')) {
    $config
      ->set('imagemagick_bin_path', $form_state
      ->getValue('file_imagemagick_bin_path'));
  }
  $config
    ->save();
}

/**
 * Implements opigno_module_get_user_module_score().
 *
 * @param \Drupal\opigno_module\Entity\OpignoModule $opigno_module
 *   Module object.
 * @param \Drupal\Core\Session\AccountInterface $account
 *   User account object.
 *
 * @return int
 *   Score in percents depends of OpignoModule keep_results option.
 */
function opigno_module_get_user_module_score(OpignoModule $opigno_module, AccountInterface $account, $latest_cert_date = NULL) {
  return $opigno_module
    ->getUserScore($account, $latest_cert_date);
}

/**
 * Implements hook_entity_access().
 */
function opigno_module_entity_access(EntityInterface $entity, $operation, AccountInterface $account) {
  if ($entity
    ->getEntityTypeId() == 'file' && $operation == 'download') {
    $user_is_manager = LearningPathAccess::memberHasRole('user_manager', $account);
    if ($user_is_manager || $account
      ->id() == $entity
      ->get('uid')
      ->getString()) {
      return AccessResult::allowed();
    }
  }
  if ($entity
    ->getEntityTypeId() == 'user_module_status') {
    if ($entity
      ->getOwnerId() == $account
      ->id()) {
      return AccessResultAllowed::allowedIf((int) $account
        ->id() === 0)
        ->mergeCacheMaxAge(0);
    }
  }
}

/**
 * Implements hook_views_view_field().
 */
function opigno_module_preprocess_views_view_field(&$variables) {
  if ($variables['view']
    ->id() == 'media_browser_file_pdf' && $variables['field']->options['id'] == 'name') {
    $variables['bundle'] = $variables['view']->field['bundle']->original_value
      ->jsonSerialize();
  }
  if ($variables['view']
    ->id() == 'module_list' && $variables['field']->options['id'] == 'name') {
    $output = $variables['output']
      ->jsonSerialize();
    if (strpos($output, $_SERVER["HTTP_HOST"] . '//') !== FALSE) {
      $output = str_replace($_SERVER["HTTP_HOST"] . '//', $_SERVER["HTTP_HOST"] . '/', $output);
      $variables['output'] = [
        '#markup' => $output,
      ];
    }
  }
}

/**
 * Implements hook_entity_operation().
 */
function opigno_module_entity_operation(\Drupal\Core\Entity\EntityInterface $entity) {
  if (!\Drupal::currentUser()
    ->hasPermission('access content')) {
    return [];
  }
  $entityTypeId = $entity
    ->getEntityTypeId();
  if ($entityTypeId !== 'opigno_module' && $entityTypeId !== 'opigno_activity') {
    return [];
  }
  switch ($entityTypeId) {
    case 'opigno_module':
      $operations['module_results'] = array(
        'title' => t('See Results'),
        'weight' => 14,
        'url' => Url::fromRoute('view.opigno_module_results.opigno_module_results', [
          'arg_0' => $entity
            ->id(),
        ]),
      );
      $operations['duplicate_module'] = array(
        'title' => t('Duplicate'),
        'weight' => 15,
        'url' => Url::fromRoute('opigno_module.duplicate', [
          'opigno_module' => $entity
            ->id(),
        ]),
      );
      $operations['export_module'] = array(
        'title' => t('Export'),
        'weight' => 16,
        'url' => Url::fromRoute('opigno_module.export.module', [
          'opigno_module' => $entity
            ->id(),
        ]),
      );
      break;
    case 'opigno_activity':
      $operations['export_activity'] = array(
        'title' => t('Export'),
        'weight' => 15,
        'url' => Url::fromRoute('opigno_module.export.activity', [
          'opigno_activity' => $entity
            ->id(),
        ]),
      );
      break;
  }
  return $operations;
}

/**
 * Implements hook_ENTITY_TYPE_update().
 */
function opigno_module_opigno_activity_update(Drupal\Core\Entity\EntityInterface $entity) {

  /* @var \Drupal\opigno_module\Entity\OpignoActivity $entity */
  $database = \Drupal::database();

  // Update module-activity relationships if new revision for activity was created.
  $query = $database
    ->select('opigno_module_relationship', 'omr')
    ->condition('omr.child_id', $entity
    ->id())
    ->fields('omr', [
    'omr_id',
    'child_id',
    'child_vid',
  ]);
  $relationships = $query
    ->execute()
    ->fetchAll();
  if ($relationships) {

    // Update each table where current activity is a part of a module.
    foreach ($relationships as $item) {
      if ($entity
        ->getRevisionId() != $item->child_vid) {
        $database
          ->update('opigno_module_relationship')
          ->condition('omr_id', $item->omr_id)
          ->fields([
          'child_vid' => $entity
            ->getRevisionId(),
        ])
          ->execute();
      }
    }
  }
}

/**
 * Implements hook_entity_delete().
 */
function opigno_module_entity_delete(EntityInterface $entity) {
  if ($entity
    ->getEntityTypeId() === 'opigno_group_content' && $entity
    ->getGroupContentTypeId() === 'ContentTypeModule' && $entity
    ->getGroup() && $entity
    ->getEntityId()) {
    $lp_module_availability = LPModuleAvailability::loadByProperties([
      'group_id' => $entity
        ->getGroup()
        ->id(),
      'entity_id' => $entity
        ->getEntityId(),
    ]);
    if ($lp_module_availability) {
      $lp_module_availability = current($lp_module_availability);
      try {
        $lp_module_availability
          ->delete();
      } catch (\Exception $e) {
        \Drupal::logger('opigno_module')
          ->error($e
          ->getMessage());
        \Drupal::messenger()
          ->addMessage($e
          ->getMessage(), 'error');
      }
    }
  }
}

/**
 * Implements hook_views_pre_render().
 *
 * @param \Drupal\views\ViewExecutable $view
 */
function opigno_module_views_pre_render(ViewExecutable $view) {
  if (isset($view) && $view
    ->id() == 'opigno_module_results') {
    $view->element['#attached']['library'][] = 'opigno_module/opigno_module_results_view';
  }
}

Functions

Namesort descending Description
opigno_module_activities_bank_lp_form_submit Custom submit for view activities bank lp form.
opigno_module_activities_bank_lp_form_validate Custom submit for view activities bank lp form.
opigno_module_entity_access Implements hook_entity_access().
opigno_module_entity_delete Implements hook_entity_delete().
opigno_module_entity_operation Implements hook_entity_operation().
opigno_module_form_alter Implements hook_form_alter().
opigno_module_form_system_file_system_settings_alter Implements hook_form_FORM_ID_alter().
opigno_module_form_system_file_system_settings_submit Implements additional submit for the file system settings form.
opigno_module_form_views_form_opigno_activities_bank_lp_interface_default_alter Ajaxify activity bank form.
opigno_module_get_activities Helper function.
opigno_module_get_users_by_role Function for loading all users by role.
opigno_module_get_user_module_score Implements opigno_module_get_user_module_score().
opigno_module_h5p_styles_alter Implements hook_h5p_styles_alter().
opigno_module_help Implements hook_help().
opigno_module_is_activity_route Helper function.
opigno_module_opigno_activity_update Implements hook_ENTITY_TYPE_update().
opigno_module_page_attachments Implements hook_page_attachments().
opigno_module_page_top Implements hook_page_top().
opigno_module_preprocess_html Implements hook_preprocess_page().
opigno_module_preprocess_page Implements hook_preprocess_page().
opigno_module_preprocess_views_view_field Implements hook_views_view_field().
opigno_module_theme Implements hook_theme().
opigno_module_theme_suggestions_alter Implements hook_theme_suggestions_alter().
opigno_module_theme_suggestions_opigno_activity Implements hook_theme_suggestions_HOOK().
opigno_module_theme_suggestions_opigno_answer Implements hook_theme_suggestions_HOOK().
opigno_module_views_form_opigno_activities_bank_lp_interface_default_ajax_callback Implements opigno_module_views_form_opigno_activities_bank_lp_interface_default_ajax_callback().
opigno_module_views_pre_render Implements hook_views_pre_render().
opigno_module_views_query_alter Implements hook_views_query_alter().
prepare_directory_structure_for_import Run before import modules, activities etc to crete directories.
template_preprocess_opigno_user_result_item User result item preprocess template.
_skills_term_validation Custom validation on creating new skill term.
_skill_level_names_ajax Ajax form submit.

Constants