View source
<?php
namespace Drupal\course\Entity;
use Drupal;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Link;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Drupal\course\Helper\CourseHandler;
use Drupal\course\Plugin\CourseObjectAccessPluginBase;
use stdClass;
use Symfony\Component\HttpFoundation\RedirectResponse;
use function course_editing_start;
use function course_get_handlers;
use function course_iframe;
use function views_embed_view;
abstract class CourseObject extends CourseHandler implements \Drupal\Core\Entity\RevisionLogInterface {
use Drupal\Core\Entity\RevisionLogEntityTrait;
use Drupal\Core\Entity\EntityChangedTrait;
protected $accessMessages = array();
public function overrideNavigation() {
return array();
}
public function hasPolling() {
return FALSE;
}
public function overrideOutlineListItem(&$item) {
}
public function isActive() {
return $this
->getCourse()
->current()
->id() == $this
->id();
}
public function optionsDefinition() {
$defaults = parent::optionsDefinition();
$defaults += array(
'uniqid' => NULL,
'cid' => NULL,
'title' => NULL,
'enabled' => 1,
'hidden' => 0,
'required' => 1,
'skippable' => 0,
'delete' => 0,
'delete_instance' => 0,
'grade_include' => 0,
'instance' => NULL,
'plugins' => array(),
'duration' => NULL,
'skippable' => 0,
);
return $defaults;
}
public function optionsForm(&$form, FormStateInterface $form_state) {
parent::optionsForm($form, $form_state);
$config = $this
->getOptions();
$form['header']['#markup'] = t("<h2>Settings for %t</h2>", array(
'%t' => $this
->getTitle(),
));
$form['uniqid'] = array(
'#type' => 'value',
'#value' => $this
->id(),
);
$form['course_tabs']['#type'] = 'vertical_tabs';
$form['course_tabs']['#default_tab'] = 'edit-title';
$form['title'] = array(
'#title' => t('Title & description'),
'#type' => 'details',
'#group' => 'course_tabs',
'#weight' => 0,
);
$form['outline'] = array(
'#type' => 'details',
'#title' => t('Settings'),
'#group' => 'course_tabs',
'#weight' => 1,
);
$form['plugins']['access'] = array(
'#type' => 'details',
'#title' => 'Access',
'#group' => 'course_tabs',
'#weight' => 4,
);
$form['delete'] = array(
'#type' => 'details',
'#title' => 'Delete',
'#group' => 'course_tabs',
'#weight' => 5,
);
$form['title']['title'] = array(
'#title' => t('Title'),
'#type' => 'textfield',
'#description' => t('The title of this course object as it will appear to users in the course outline.'),
'#size' => 100,
'#default_value' => $config['title'],
'#group' => 'description',
'#required' => TRUE,
);
$form['outline']['enabled'] = array(
'#title' => t('Enabled'),
'#type' => 'checkbox',
'#description' => t('Enabled course objects will become part of the course. Uncheck this box if you are not using this course object.'),
'#default_value' => $config['enabled'],
);
$form['outline']['hidden'] = array(
'#title' => t('Visible in outline'),
'#type' => 'checkbox',
'#description' => t('Objects that are not visible will not be seen by the learner. Uncheck this box for course objects that you do not want the learner to see.'),
'#default_value' => !$config['hidden'],
'#group' => 'course',
);
$form['outline']['required'] = array(
'#title' => t('Completion required'),
'#type' => 'checkbox',
'#description' => t('Users must complete required objects. Uncheck this box if this is an optional course object.'),
'#default_value' => $config['required'],
);
$form['outline']['skippable'] = array(
'#title' => t('Skippable'),
'#type' => 'checkbox',
'#default_value' => $config['skippable'],
'#states' => array(
'visible' => array(
'#edit-required' => array(
'checked' => TRUE,
),
),
),
'#description' => t('Users can proceed past this object but it will still be required for course completion.'),
);
$form['delete']['delete_button'] = array(
'#value' => t('Delete'),
'#weight' => 999,
'#type' => 'submit',
'#submit' => array(
array(
$this,
'setDelete',
),
array(
$this,
'optionsSubmit',
),
),
'#limit_validation_errors' => array(),
);
if (!empty($config['instance'])) {
$form['delete']['delete_instance'] = array(
'#title' => t('Also delete the referenced content.'),
'#type' => 'checkbox',
'#default_value' => $config['delete_instance'],
'#stats' => array(
'visible' => array(
'#edit-delete' => array(
'checked' => TRUE,
),
),
),
'#group' => 'delete',
);
if (Drupal::database()
->query("SELECT count(coid) FROM {course_object} WHERE object_type = :object_type AND instance = :instance", array(
':object_type' => $config['object_type'],
':instance' => $config['instance'],
))
->fetchField() > 1) {
$form['delete']['delete_instance']['#description'] = t('<span class="error"><strong>WARNING</strong></span>: multiple course objects link to this instance. Deleting the instance might break the other course objects that use it.');
}
}
if ($this
->isGraded()) {
$form['grading'] = array(
'#title' => t('Grading'),
'#type' => 'details',
'#description' => t('Settings for graded objects.'),
'#group' => 'course_tabs',
'#weight' => 2,
);
$form['grading']['grade_include'] = array(
'#title' => t('Include in final course grade'),
'#description' => t('Include this grade result for calculation of the final course grade.<br/>Currently, only the last grade result per Course will be used.'),
'#default_value' => $config['grade_include'],
'#type' => 'checkbox',
);
}
$form['plugins']['#tree'] = TRUE;
$form['plugins']['access']['#title'] = t('Access');
$form['plugins']['access']['#description'] = t('By default, all required objects appearing before this object in the course outline must be completed before the user may access this object. Conditional access allows for additional conditions to be applied.');
$form['plugins']['access']['#type'] = 'details';
$pluginManager = Drupal::service('plugin.manager.course.object.access');
$plugins = $pluginManager
->getDefinitions();
foreach ($plugins as $key => $plugin) {
$form['plugins']['access']['#tree'] = TRUE;
$form['plugins']['access'][$key] = array(
'#title' => $plugin['label'],
'#type' => 'details',
'#tree' => TRUE,
'#open' => FALSE,
);
$courseAccess = $pluginManager
->createInstance($key);
$courseAccess
->setCourseObject($this);
$courseAccess
->setType($key);
$access_form = [];
$access_form_state = new FormState();
$form['plugins']['access'][$key] += $courseAccess
->buildConfigurationForm($access_form, $access_form_state);
}
$form['actions']['update'] = array(
'#value' => t('Update'),
'#weight' => 999,
'#type' => 'submit',
'#validate' => array(
array(
$this,
'optionsValidate',
),
),
'#submit' => array(
array(
$this,
'optionsSubmit',
),
),
);
}
public function setDelete(&$form, FormStateInterface $form_state) {
$form_state
->setValue('delete', 1);
if (!empty($form_state
->getUserInput()['delete_instance'])) {
$form_state
->setValue('delete_instance', $form_state
->getUserInput()['delete_instance']);
}
}
public function optionsValidate(&$form, FormStateInterface $form_state) {
}
public function optionsSubmit(&$form, FormStateInterface $form_state) {
$uniqid = $this
->id();
$course = $form_state
->getBuildInfo()['args'][0];
$cid = $course
->id();
course_editing_start($this
->getCourse());
if ($form_state
->getValue('hidden')) {
$form_state
->setValue('hidden', $form_state
->getValue('hidden') != 1);
}
$_SESSION['course'][$cid]['editing'][$uniqid] = $form_state
->getValues() + $_SESSION['course'][$cid]['editing'][$uniqid];
}
public function getOptionsSummary() {
$summary = parent::getOptionsSummary();
$options = $this
->getOptions();
if ($options['enabled']) {
$summary['enabled'] = t('Enabled');
}
else {
$summary['enabled'] = '<span class="warning">' . t('Not enabled') . '</span>';
}
if (!$options['hidden']) {
$summary['hidden'] = t('Visible in outline');
}
else {
$summary['hidden'] = '<span class="warning">' . t('Not visible in outline') . '</span>';
}
if ($options['required']) {
$summary['required'] = t('Completion required');
if ($options['skippable']) {
$summary['skippable'] = '<span class="warning">' . t('Skippable') . '</span>';
}
}
else {
$summary['required'] = '<span class="warning">' . t('Completion not required') . '</span>';
}
if ($this
->getInstanceId()) {
if ($viewUrl = $this
->getViewUrl()) {
$text = t('View instance');
$summary['instance_view'] = Link::fromTextAndUrl($text, $viewUrl)
->toString();
}
if ($editUrl = $this
->getEditUrl()) {
$text = t('Edit instance');
$summary['instance'] = Link::fromTextAndUrl($text, $editUrl)
->toString();
}
elseif ($this
->isTemporary()) {
$summary['instance'] = '<span class="warning">' . t('Save course to edit object') . '</span>';
}
}
if (!empty($options['delete'])) {
$dest = Url::fromRoute('course.object.restore', [
'course' => $options['cid'],
'course_object' => $this
->id(),
], [
'attributes' => [
'class' => 'use-ajax',
],
]);
$text = t('Object will be removed from outline');
$restore_text = t('Restore this object to the course outline.');
if ($options['delete_instance']) {
$text = t('Object will be removed from outline, and related instance(s) will be deleted');
$restore_text = t('Restore this object and related instance(s) to the course outline.');
}
$restore = Link::fromTextAndUrl(t('Restore'), $dest, $restore_text)
->toString();
$summary['delete'] = '<span class="error">' . $text . '</span>';
$summary['restore'] = $restore;
}
return $summary;
}
public function renderOptionsSummary() {
$summary = $this
->getOptionsSummary();
$out = [];
foreach ($summary as $key => $item) {
$out[$key]['#markup'] = $item;
}
return $out;
}
public function getOptions() {
$options = parent::getOptions();
$sessionDefaults = array();
$cid = $this
->getCourse()
->id();
$coid = $this
->id();
if (!empty($_SESSION['course'][$cid]['editing'][$coid])) {
$sessionDefaults += array_filter((array) $_SESSION['course'][$cid]['editing'][$coid], array(
$this,
'optionFilter',
));
unset($sessionDefaults['weight']);
}
return array_merge($options, (array) $sessionDefaults);
}
public function getReadOnlyOptions() {
$account = \Drupal::currentUser();
if (!isset($this->readOnlyOptionsCache[$account
->id()])) {
$options = $this
->getOptions();
$pluginManager = Drupal::service('plugin.manager.course.object.access');
$plugins = $pluginManager
->getDefinitions();
foreach ($plugins as $key => $plugin) {
$accessPlugin = $pluginManager
->createInstance($key);
$accessPlugin
->setCourseObject($this);
$accessPlugin
->setType($key);
$accessPlugin
->alterOptions($options, $account);
}
$this->readOnlyOptionsCache[$account
->id()] = $options;
}
return $this->readOnlyOptionsCache[$account
->id()];
}
public function getReadOnlyOption($key) {
return $this
->getReadOnlyOptions()[$key];
}
private function optionFilter($a) {
return !is_null($a);
}
public final function takeObject() {
$account = Drupal::currentUser();
$_SESSION['course']['active'] = $this
->getCourse()
->id();
$_SESSION['course'][$this
->getCourse()
->id()]['taking']['active'] = $this
->id();
if ($this
->access('take')) {
$this
->getFulfillment($account)
->grant();
$this
->getFulfillment($account)
->save();
}
else {
$this
->getFulfillment($account)
->revoke();
return FALSE;
}
$out = $this
->take();
$url = $this
->getTakeUrl();
switch ($this
->getTakeType()) {
case 'iframe':
return course_iframe($url);
case 'popup':
return "will popup {$url}";
case 'content':
return $out;
case 'redirect':
default:
return new RedirectResponse($url
->toString());
}
}
public function getTakeType() {
return 'content';
}
public function take() {
return t('This should be overridden by the module to return course content.');
}
public function getUrl() {
return Url::fromRoute('course.object', [
'course' => $this
->getCourse()
->id(),
'course_object' => $this
->id(),
]);
}
protected function getTakeUrl() {
}
public function getEditUrl() {
}
public function getViewUrl() {
}
public function isRequired() {
return (bool) $this
->getReadOnlyOption('required');
}
public function isSkippable() {
return (bool) $this
->getReadOnlyOption('skippable');
}
public function isEnabled() {
return (bool) $this
->getReadOnlyOption('enabled');
}
public function isVisible() {
return (bool) (!$this
->getReadOnlyOption('hidden'));
}
function isGraded() {
return FALSE;
}
public function getStatus() {
}
public function getFulfillment(AccountInterface $account) {
$entities = \Drupal::entityTypeManager()
->getStorage('course_object_fulfillment')
->loadByProperties([
'coid' => $this
->id(),
'uid' => $account
->id(),
]);
if ($entities) {
return reset($entities);
}
else {
return CourseObjectFulfillment::create(array(
'coid' => $this
->id(),
'uid' => $account
->id(),
'object_type' => $this
->get('object_type')
->getString(),
));
}
}
function getInstanceId() {
return $this
->getOption('instance');
}
function setInstanceId($id) {
return $this
->setOption('instance', $id);
}
public function setCourse($course) {
if (is_numeric($course)) {
$this
->setOption('cid', $course);
}
else {
$this
->setOption('cid', $course
->id());
}
return $this;
}
function getCourse() {
return Course::load($this
->get('cid')
->getString());
}
function getComponentName() {
$handlers = course_get_handlers('object');
return $handlers[$this
->getComponent()]['label'];
}
function getComponent() {
return $this
->getOption('object_type');
}
function setComponent($component) {
return $this
->setOption('object_type', $component);
}
function setId($coid) {
return $this
->setOption('coid', $coid);
}
public function createInstance() {
}
public function deleteInstance() {
}
function getTitle() {
$object_info = course_get_handlers('object');
if (!$this
->getOption('title')) {
$title = $object_info[$this
->getOption('object_type')]['label'];
$this
->setOption('title', $title);
}
return $this
->getOption('title');
}
function poll() {
}
function getReports() {
return array(
'default' => array(
'title' => 'Overview',
),
);
}
function getReport($key) {
if ($key == 'default') {
return array(
'title' => 'Overview',
'content' => views_embed_view('course_object_report', 'default', $this
->getCourse()
->id(), $this
->id()),
);
}
}
function freeze() {
}
function thaw($ice) {
}
function getCloneAbility() {
return FALSE;
}
public function save() {
$this
->getTitle();
if ($ice = $this
->getOption('freeze')) {
$this
->setInstanceId($this
->thaw($ice));
$this
->setOption('freeze', NULL);
}
foreach ($this
->optionsDefinition() as $key => $default) {
$value = $this
->getOption($key);
$this
->set($key, $value);
}
if (!$this
->getInstanceId()) {
$this
->createInstance();
}
if (strpos($this
->id(), 'course_object_') !== FALSE) {
$this
->setId(NULL);
}
$data = $this
->get('data')
->getValue();
return parent::save();
}
function isTemporary() {
return strpos($this
->id(), 'course_object_') === 0;
}
public static function getMaxOccurences() {
return FALSE;
}
public static function context() {
}
public function uri() {
return array(
'path' => 'course/' . $this
->getCourse()
->id() . '/object/' . $this
->id(),
);
}
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields = parent::baseFieldDefinitions($entity_type);
$fields += static::revisionLogBaseFieldDefinitions($entity_type);
$fields['cid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Course'));
$fields['object_type'] = BaseFieldDefinition::create('string')
->setLabel(t('Object'));
$fields['title'] = BaseFieldDefinition::create('string')
->setDisplayConfigurable('view', TRUE)
->setRevisionable(TRUE)
->setLabel(t('Object title'));
$fields['enabled'] = BaseFieldDefinition::create('boolean')
->setRevisionable(TRUE)
->setLabel(t('Course ID'));
$fields['instance'] = BaseFieldDefinition::create('string')
->setRevisionable(TRUE)
->setLabel(t('Instance identifier'))
->setDescription('An ID used to identify a remote activity.');
$fields['required'] = BaseFieldDefinition::create('boolean')
->setRevisionable(TRUE)
->setDisplayConfigurable('view', TRUE)
->setLabel(t('Required'));
$fields['weight'] = BaseFieldDefinition::create('integer')
->setRevisionable(TRUE)
->setLabel(t('Weight'));
$fields['hidden'] = BaseFieldDefinition::create('boolean')
->setRevisionable(TRUE)
->setLabel(t('Hidden'));
$fields['duration'] = BaseFieldDefinition::create('integer')
->setRevisionable(TRUE)
->setLabel(t('Duration'));
$fields['data'] = BaseFieldDefinition::create('map')
->setLabel(t('Data'));
$fields['created'] = BaseFieldDefinition::create('created')
->setRevisionable(TRUE)
->setLabel('Created');
$fields['changed'] = BaseFieldDefinition::create('changed')
->setRevisionable(TRUE)
->setLabel('Changed');
return $fields;
}
function set($name, $value, $notify = TRUE) {
if (!in_array($name, array_keys($this
->getFieldDefinitions()))) {
$extra = parent::get('data')
->getValue() ?? [];
$extra[0][$name] = $value;
return parent::set('data', $extra[0]);
}
else {
return parent::set($name, $value, $notify);
}
}
public function getEntityType() {
$entityType = parent::getEntityType();
$class = get_class($this);
$entityType
->set('originalClass', $class);
return $entityType;
}
public static function postDelete(EntityStorageInterface $storage, array $entities) {
$fs = \Drupal::entityTypeManager()
->getStorage('course_object_fulfillment');
$coids = array_keys($entities);
$fulfillments = $fs
->loadByProperties([
'coid' => $coids,
]);
$fs
->delete($fulfillments);
parent::postDelete($storage, $entities);
}
public function postSave(EntityStorageInterface $storage, $update = TRUE) {
parent::postSave($storage, $update);
\Drupal::entityTypeManager()
->getAccessControlHandler('course_object')
->resetCache();
}
}