diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2ee3b828 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,14 @@ +# Change log + +## Version 1.2 (2042-01-21) +- fixed error with ascii control characters when written text is processed +- fixed juristic headline scheme +- added settings for processing written text (paragraph numbers, correction margins) +- added settings for pdf generation (heqader, footer, margins) +- improved pdf layout for written text and correction +- check existence of corrector comments for pdf upload and editor settings +- removed "Pilot" from language variables + +## Version 1.1 (2023-12-20) + +- First published version of the plugin for ILIAS 7 \ No newline at end of file diff --git a/README.md b/README.md index 679d1cbf..3283bad6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,13 @@ -# LongEssayAssessment (Pilot Version) +# LongEssayAssessment Plugin for the LMS ILIAS open source to realize exams with writing of long texts. -This pilot version is currently **under development** to complete the functionality required by the [EDUTIEK project](https://www.edutiek.de). +The EDUTIEK project (acronym for "Einfache Durchführung textintensiver E-Klausuren") is developing a comprehensive software solution for online exams in subjects in which longer texts have to be submitted as exam solutions. These include law, history, linguistics, philosophy, sociology and many more. -An initial set of features is available in the **pre-test version** named LongEssayTask which is maintained in a different [GitHub repository](https://github.com/fneumann/LongEssayTask). +The "Long Essay Assessment" is a repository object and bundles all functions for the realisation of a text exam. Responsibilities for creating, carrying out and correcting tasks are assigned to different people via the authorisation system. Support material can be provided for editing and correction is supported by an evaluation scheme. All results can be output in PDF/A format for documentation purposes. + +The integrated "Writer" is a specialised editing page for examinees during the exam. The text editor and the task or additional material can be displayed side by side or on a full page. All editing steps are logged and are reversible. Even if the network is interrupted, you can continue writing and the editing steps will be saved afterwards. At the end of the editing time, the written text is displayed for review and its submission is finally confirmed. + +The integrated "Corrector" is a specialised editing page for the proofreaders. In the submitted text, passages are marked and provided with comments. With each comment, partial points can be awarded based on the evaluation scheme. The text and comments are clearly displayed next to each other, optionally also with the comments from the first correction in the case of a second correction. To create the overall vote, a proposal for the final grade is calculated from the sum of the partial points, which can be accepted or changed. The vote can be used to create a textual overall assessment. ## Installation @@ -22,8 +26,19 @@ An initial set of features is available in the **pre-test version** named LongEs composer install --no-dev ```` -## History +Please clear your browser cache after an update before you start the writing and ccorrection screens. + +## Branches and Versions + +The plugin is published for ILIAS in different branches: + +* **release1_ilias7** will receive bug fixes only +* **release2_ilias8** will be created by March 2024 +* **main** is the current development branch. Please do not use it for production. + +Versions 2.x will receive bug fixes as well as new features without breaking existing functionality and data. +Please consult the [CHANGELOG](CHANGELOG.md) to see the different versions. -### Version 1.1 (2023-12-20) +## Known Issues -- First published version of the plugin for ILIAS 7 \ No newline at end of file +The writing and correction of exams is tested with Firefox and Chrome, so modern Chromium based browser should work. We know about issues with older Safari browsers. Please test with you local system before writing an exam and offer a tryout service for students who should write on their own device. \ No newline at end of file diff --git a/classes/Corrector/class.CorrectorContext.php b/classes/Corrector/class.CorrectorContext.php index 93d4337b..ab72bc3d 100644 --- a/classes/Corrector/class.CorrectorContext.php +++ b/classes/Corrector/class.CorrectorContext.php @@ -22,7 +22,7 @@ use ILIAS\Plugin\LongEssayAssessment\Data\Essay\CorrectorComment; use ILIAS\Plugin\LongEssayAssessment\Data\Essay\CriterionPoints; use ILIAS\Plugin\LongEssayAssessment\Data\Task\CorrectionSettings as PluginCorrectionSettings; -use Edutiek\LongEssayAssessmentService\Data\CorrectionPage; +use Edutiek\LongEssayAssessmentService\Data\PageData; use Edutiek\LongEssayAssessmentService\Data\CorrectionMark; use Edutiek\LongEssayAssessmentService\Data\CorrectionPreferences; use ILIAS\Plugin\LongEssayAssessment\Data\Corrector\CorrectorPreferences; @@ -518,9 +518,8 @@ public function getPagesOfItem(string $item_key): array (int) $item_key, $this->task->getTaskId())) ) { foreach ($essay_repo->getEssayImagesByEssayID($repoEssay->getId()) as $repoImage) { - $pages[] = new CorrectionPage( + $pages[] = new PageData( (string) $repoImage->getId(), - $item_key, $repoImage->getPageNo(), $repoImage->getWidth(), $repoImage->getHeight(), diff --git a/classes/Corrector/class.CorrectorStartGUI.php b/classes/Corrector/class.CorrectorStartGUI.php index f0f7cef3..fc2a4328 100644 --- a/classes/Corrector/class.CorrectorStartGUI.php +++ b/classes/Corrector/class.CorrectorStartGUI.php @@ -418,7 +418,7 @@ protected function downloadWrittenPdf() $params = $this->request->getQueryParams(); $writer_id = (int) ($params['writer_id'] ?? 0); - $service = $this->localDI->getCorrectorAdminService($this->object->getId()); + $service = $this->localDI->getWriterAdminService($this->object->getId()); $repoWriter = $this->localDI->getWriterRepo()->getWriterById($writer_id); $filename = 'task' . $this->object->getId() . '_writer' . $repoWriter->getId(). '-writing.pdf'; diff --git a/classes/CorrectorAdmin/class.CorrectorAdminGUI.php b/classes/CorrectorAdmin/class.CorrectorAdminGUI.php index af1b261b..69a2105a 100644 --- a/classes/CorrectorAdmin/class.CorrectorAdminGUI.php +++ b/classes/CorrectorAdmin/class.CorrectorAdminGUI.php @@ -434,7 +434,7 @@ protected function downloadWrittenPdf() $params = $this->request->getQueryParams(); $writer_id = (int) ($params['writer_id'] ?? 0); - $service = $this->localDI->getCorrectorAdminService($this->object->getId()); + $service = $this->localDI->getWriterAdminService($this->object->getId()); $repoWriter = $this->localDI->getWriterRepo()->getWriterById($writer_id); $filename = 'task' . $this->object->getId() . '_writer' . $repoWriter->getId(). '-writing.pdf'; diff --git a/classes/CorrectorAdmin/class.CorrectorAdminService.php b/classes/CorrectorAdmin/class.CorrectorAdminService.php index c683fce7..1eaf02f2 100644 --- a/classes/CorrectorAdmin/class.CorrectorAdminService.php +++ b/classes/CorrectorAdmin/class.CorrectorAdminService.php @@ -10,7 +10,6 @@ use ILIAS\Plugin\LongEssayAssessment\Data\Task\CorrectionSettings; use ILIAS\Plugin\LongEssayAssessment\Data\Corrector\Corrector; use ILIAS\Plugin\LongEssayAssessment\Data\Corrector\CorrectorAssignment; -use ILIAS\Plugin\LongEssayAssessment\Data\Corrector\CorrectorRepository; use ILIAS\Plugin\LongEssayAssessment\Data\Essay\CorrectorSummary; use ILIAS\Plugin\LongEssayAssessment\Data\DataService; use ILIAS\Plugin\LongEssayAssessment\Data\Essay\Essay; @@ -18,9 +17,7 @@ use ILIAS\Plugin\LongEssayAssessment\Data\Object\GradeLevel; use ILIAS\Plugin\LongEssayAssessment\Data\Task\LogEntry; use ILIAS\Plugin\LongEssayAssessment\Data\Task\TaskRepository; -use ILIAS\Plugin\LongEssayAssessment\Data\Task\TaskSettings; use ILIAS\Plugin\LongEssayAssessment\Data\Writer\Writer; -use ILIAS\Plugin\LongEssayAssessment\Data\Writer\WriterRepository; use ILIAS\Data\UUID\Factory as UUID; use ilObjUser; @@ -429,6 +426,7 @@ public function createCorrectionsExport(\ilObjLongEssayAssessment $object) : str $storage->createDir($zipdir); $repoTask = $this->taskRepo->getTaskSettingsById($object->getId()); + $writerAdminService = $this->localDI->getWriterAdminService($repoTask->getTaskId()); foreach ($this->essayRepo->getEssaysByTaskId($repoTask->getTaskId()) as $repoEssay) { $repoWriter = $this->writerRepo->getWriterById($repoEssay->getWriterId()); @@ -436,7 +434,7 @@ public function createCorrectionsExport(\ilObjLongEssayAssessment $object) : str $storage->createDir($zipdir . '/' . $subdir); $filename = $subdir . '-writing.pdf'; - $storage->write($zipdir . '/' . $subdir. '/'. $filename, $this->getWritingAsPdf($object, $repoWriter)); + $storage->write($zipdir . '/' . $subdir. '/'. $filename, $writerAdminService->getWritingAsPdf($object, $repoWriter)); $filename = $subdir . '-correction.pdf'; $storage->write($zipdir . '/' . $subdir. '/'. $filename, $this->getCorrectionAsPdf($object, $repoWriter)); @@ -453,32 +451,7 @@ public function createCorrectionsExport(\ilObjLongEssayAssessment $object) : str //$delivery = new \ilFileDelivery() } - /** - * Get the writing of an essay as PDF string - */ - public function getWritingAsPdf(\ilObjLongEssayAssessment $object, Writer $repoWriter, $anonymous = false) - { - $context = new CorrectorContext(); - $context->init((string) $this->dic->user()->getId(), (string) $object->getRefId()); - $writingTask = $context->getWritingTaskByWriterId($repoWriter->getId()); - if ($anonymous) { - $writingTask = $writingTask->withWriterName($repoWriter->getPseudonym()); - } - $writtenEssay = $context->getEssayOfItem((string) $repoWriter->getId()); - - $item = new DocuItem( - (string) $repoWriter->getId(), - $writingTask, - $writtenEssay, - [], - [], - ); - - $service = new Service($context); - return $service->getWritingAsPdf($item); - } - /** * Get the correction of an essay as PDF string * if a repo corrector is given as parameter, then only this correction is included, not other correctors diff --git a/classes/Data/Essay/class.EssayRepository.php b/classes/Data/Essay/class.EssayRepository.php index 6105f2e9..f3847b4f 100644 --- a/classes/Data/Essay/class.EssayRepository.php +++ b/classes/Data/Essay/class.EssayRepository.php @@ -229,16 +229,26 @@ public function getCorrectorCommentById(int $id) : ?RecordData /** * @param int $essay_id - * @param int $corrector_id + * @param int|null $corrector_id * @return CorrectorComment[] */ - public function getCorrectorCommentsByEssayIdAndCorrectorId(int $essay_id, int $corrector_id): array + public function getCorrectorCommentsByEssayIdAndCorrectorId(int $essay_id, ?int $corrector_id = null): array { - $query = "SELECT * FROM " . CorrectorComment::tableName() . " WHERE essay_id = " . $this->db->quote($essay_id, 'integer') . - " AND corrector_id = ". $this->db->quote($corrector_id, 'integer'); + $query = "SELECT * FROM " . CorrectorComment::tableName() . " WHERE essay_id = " . $this->db->quote($essay_id, 'integer'); + if (isset($corrector_id)) { + $query .= " AND corrector_id = ". $this->db->quote($corrector_id, 'integer');; + } return $this->queryRecords($query, CorrectorComment::model()); } + public function hasCorrectorCommentsByTaskId(int $task_id) + { + $query = "SELECT c.id FROM xlas_corrector_comment m JOIN xlas_corrector c ON m.corrector_id = c.id WHERE c.task_id = " + . $this->db->quote($task_id, 'integer') . ' LIMIT 1'; + + return !empty($this->getIntegerList($query, 'id')); + } + /** * @param int $id * @return CriterionPoints|null diff --git a/classes/Data/Object/class.ObjectRepository.php b/classes/Data/Object/class.ObjectRepository.php index 5e4a32e1..1d027d47 100644 --- a/classes/Data/Object/class.ObjectRepository.php +++ b/classes/Data/Object/class.ObjectRepository.php @@ -31,12 +31,12 @@ public function __construct( } /** - * @return ObjectSettings|null + * @return ObjectSettings */ - public function getObjectSettingsById(int $a_id): ?RecordData + public function getObjectSettingsById(int $a_id): RecordData { $query = "SELECT * FROM xlas_object_settings WHERE obj_id = " . $this->db->quote($a_id, 'integer'); - return $this->getSingleRecord($query, ObjectSettings::model()); + return $this->getSingleRecord($query, ObjectSettings::model(), new ObjectSettings($a_id)); } public function ifGradeLevelExistsById(int $a_id): bool diff --git a/classes/Data/Task/class.EditorSettings.php b/classes/Data/Task/class.EditorSettings.php index 1623250d..d640da5c 100644 --- a/classes/Data/Task/class.EditorSettings.php +++ b/classes/Data/Task/class.EditorSettings.php @@ -29,7 +29,11 @@ class EditorSettings extends RecordData 'headline_scheme'=> 'text', 'formatting_options' => 'text', 'notice_boards' => 'integer', - 'copy_allowed' => 'integer' + 'copy_allowed' => 'integer', + 'add_paragraph_numbers' => 'integer', + 'add_correction_margin' => 'integer', + 'left_correction_margin' => 'integer', + 'right_correction_margin' => 'integer' ]; protected int $task_id; @@ -37,6 +41,10 @@ class EditorSettings extends RecordData protected string $formatting_options = self::FORMATTING_OPTIONS_MEDIUM; protected int $notice_boards = 0; protected int $copy_allowed = 0; + protected int $add_paragraph_numbers = 1; + protected int $add_correction_margin = 0; + protected int $left_correction_margin = 0; + protected int $right_correction_margin = 0; public function __construct(int $task_id) { @@ -47,68 +55,48 @@ public static function model() { return new self(0); } - /** - * @return string - */ public function getHeadlineScheme(): string { - return (string)$this->headline_scheme; + return $this->headline_scheme; } - /** - * @param ?string $headline_scheme - */ - public function setHeadlineScheme(?string $headline_scheme): void + public function setHeadlineScheme(?string $headline_scheme): self { $this->headline_scheme = (string)$headline_scheme; + return $this; } - /** - * @return string - */ public function getFormattingOptions(): string { - return (string)$this->formatting_options; + return $this->formatting_options; } - /** - * @param ?string $formatting_options - */ - public function setFormattingOptions(?string $formatting_options): void + public function setFormattingOptions(?string $formatting_options): self { - $this->formatting_options = (string)$formatting_options; + $this->formatting_options = (string) $formatting_options; + return $this; } - /** - * @return int - */ public function getNoticeBoards(): int { - return (int)$this->notice_boards; + return $this->notice_boards; } - /** - * @param ?int $notice_boards - */ - public function setNoticeBoards(?int $notice_boards): void + public function setNoticeBoards(?int $notice_boards): self { $this->notice_boards = $notice_boards; + return $this; } - /** - * @return bool - */ public function isCopyAllowed(): bool { - return (bool)$this->copy_allowed; + return (bool )$this->copy_allowed; } - /** - * @param ?bool $copy_allowed - */ - public function setCopyAllowed(?bool $copy_allowed): void + public function setCopyAllowed(?bool $copy_allowed): self { - $this->copy_allowed = (int)$copy_allowed; + $this->copy_allowed = (int) $copy_allowed; + return $this; } /** @@ -119,13 +107,53 @@ public function getTaskId(): int return $this->task_id; } - /** - * @param int $task_id - * @return EditorSettings - */ - public function setTaskId(int $task_id): EditorSettings + public function setTaskId(int $task_id): self { $this->task_id = $task_id; return $this; } + + public function getAddParagraphNumbers() : bool + { + return (bool) $this->add_paragraph_numbers; + } + + public function setAddParagraphNumbers(bool $add_paragraph_numbers) : self + { + $this->add_paragraph_numbers = (int) $add_paragraph_numbers; + return $this; + } + + public function getAddCorrectionMargin() : bool + { + return (bool) $this->add_correction_margin; + } + + public function setAddCorrectionMargin(bool $add_correction_margin) : self + { + $this->add_correction_margin = (int) $add_correction_margin; + return $this; + } + + public function getLeftCorrectionMargin() : int + { + return $this->left_correction_margin; + } + + public function setLeftCorrectionMargin(int $left_correction_margin) : self + { + $this->left_correction_margin = $left_correction_margin; + return $this; + } + + public function getRightCorrectionMargin() : int + { + return $this->right_correction_margin; + } + + public function setRightCorrectionMargin(int $right_correction_margin) : self + { + $this->right_correction_margin = $right_correction_margin; + return $this; + } } \ No newline at end of file diff --git a/classes/Data/Task/class.PdfSettings.php b/classes/Data/Task/class.PdfSettings.php new file mode 100644 index 00000000..2d5fb8f8 --- /dev/null +++ b/classes/Data/Task/class.PdfSettings.php @@ -0,0 +1,124 @@ + + */ +class PdfSettings extends RecordData +{ + protected const tableName = 'xlas_pdf_settings'; + protected const hasSequence = false; + protected const keyTypes = [ + 'task_id' => 'integer', + ]; + protected const otherTypes = [ + 'add_header' => 'integer', + 'add_footer' => 'integer', + 'top_margin' => 'integer', + 'bottom_margin' => 'integer', + 'left_margin' => 'integer', + 'right_margin' => 'integer' + ]; + + protected int $task_id; + protected int $add_header = 1; + protected int $add_footer = 1; + protected int $top_margin = 10; + protected int $bottom_margin = 10; + protected int $left_margin = 10; + protected int $right_margin = 10; + + public function __construct(int $task_id) + { + $this->task_id = $task_id; + } + + public static function model() { + return new self(0); + } + + /** + * @return int + */ + public function getTaskId(): int + { + return $this->task_id; + } + + public function setTaskId(int $task_id): self + { + $this->task_id = $task_id; + return $this; + } + + public function getAddHeader() : bool + { + return (bool) $this->add_header; + } + + public function setAddHeader(bool $add_header) : self + { + $this->add_header = (int) $add_header; + return $this; + } + + public function getAddFooter() : bool + { + return (bool) $this->add_footer; + } + + public function setAddFooter(bool $add_footer) : self + { + $this->add_footer = (int) $add_footer; + return $this; + } + + + public function getTopMargin() : int + { + return $this->top_margin; + } + + public function setTopMargin(int $top_margin) : self + { + $this->top_margin = $top_margin; + return $this; + } + + public function getBottomMargin() : int + { + return $this->bottom_margin; + } + + public function setBottomMargin(int $bottom_margin) : self + { + $this->bottom_margin = $bottom_margin; + return $this; + } + + public function getLeftMargin() : int + { + return $this->left_margin; + } + + public function setLeftMargin(int $left_margin) : self + { + $this->left_margin = $left_margin; + return $this; + } + + public function getRightMargin() : int + { + return $this->right_margin; + } + + public function setRightMargin(int $right_margin) : self + { + $this->right_margin = $right_margin; + return $this; + } +} \ No newline at end of file diff --git a/classes/Data/Task/class.TaskRepository.php b/classes/Data/Task/class.TaskRepository.php index 86b7d4b4..ec692757 100644 --- a/classes/Data/Task/class.TaskRepository.php +++ b/classes/Data/Task/class.TaskRepository.php @@ -32,38 +32,42 @@ public function __construct(\ilDBInterface $db, /** * Save record data of an allowed type - * @param TaskSettings|EditorSettings|CorrectionSettings|Alert|Resource|Location $record + * @param TaskSettings|EditorSettings|PdfSettings|CorrectionSettings|Alert|Resource|Location $record */ public function save(RecordData $record) { $this->replaceRecord($record); } - public function createTask(TaskSettings $a_task_settings, EditorSettings $a_editor_settings, CorrectionSettings $a_correction_settings) - { - $this->save($a_task_settings); - $this->save($a_editor_settings); - $this->save($a_correction_settings); - } - /** * @param int $a_id - * @return EditorSettings|null + * @return EditorSettings */ - public function getEditorSettingsById(int $a_id): ?RecordData + public function getEditorSettingsById(int $a_id): RecordData { $query = "SELECT * FROM xlas_editor_settings WHERE task_id = " . $this->db->quote($a_id, 'integer'); - return $this->getSingleRecord($query, EditorSettings::model()); + return $this->getSingleRecord($query, EditorSettings::model(), new EditorSettings($a_id)); } - /** + /** + * @param int $a_id + * @return PdfSettings + */ + public function getPdfSettingsById(int $a_id): RecordData + { + $query = "SELECT * FROM xlas_pdf_settings WHERE task_id = " . $this->db->quote($a_id, 'integer'); + return $this->getSingleRecord($query, PdfSettings::model(), new PdfSettings($a_id)); + } + + + /** * @param int $a_id - * @return CorrectionSettings|null + * @return CorrectionSettings */ - public function getCorrectionSettingsById(int $a_id): ?RecordData + public function getCorrectionSettingsById(int $a_id): RecordData { $query = "SELECT * FROM xlas_corr_setting WHERE task_id = " . $this->db->quote($a_id, 'integer'); - return $this->getSingleRecord($query, CorrectionSettings::model()); + return $this->getSingleRecord($query, CorrectionSettings::model(), new CorrectionSettings($a_id)); } @@ -74,12 +78,12 @@ public function ifTaskExistsById(int $a_id): bool /** * @param int $a_id - * @return TaskSettings|null + * @return TaskSettings */ - public function getTaskSettingsById(int $a_id): ?RecordData + public function getTaskSettingsById(int $a_id): RecordData { $query = "SELECT * FROM xlas_task_settings WHERE task_id = " . $this->db->quote($a_id, 'integer'); - return $this->getSingleRecord($query, TaskSettings::model()); + return $this->getSingleRecord($query, TaskSettings::model(), new TaskSettings($a_id)); } public function ifAlertExistsById(int $a_id): bool @@ -108,6 +112,8 @@ public function deleteTask(int $a_id) $this->db->manipulate("DELETE FROM xlas_task_settings" . " WHERE task_id = " . $this->db->quote($a_id, "integer")); $this->db->manipulate("DELETE FROM xlas_editor_settings" . + " WHERE task_id = " . $this->db->quote($a_id, "integer")); + $this->db->manipulate("DELETE FROM xlas_pdf_settings" . " WHERE task_id = " . $this->db->quote($a_id, "integer")); $this->db->manipulate("DELETE FROM xlas_corr_setting" . " WHERE task_id = " . $this->db->quote($a_id, "integer")); diff --git a/classes/Data/class.DataService.php b/classes/Data/class.DataService.php index bfe1f7e2..4e5dca66 100644 --- a/classes/Data/class.DataService.php +++ b/classes/Data/class.DataService.php @@ -537,11 +537,6 @@ public function formatCorrectionInclusions (CorrectorSummary $summary, Corrector . $this->plugin->txt('include_criteria_points') . ' ' . $this->plugin->txt($inclusion == CorrectorSummary::INCLUDE_INFO ? 'include_suffix_info' : 'include_suffix_relevant'); } - if (($inclusion = $summary->getIncludeWriterNotes() ?? $preferences->getIncludeWriterNotes()) > CorrectorSummary::INCLUDE_NOT) { - $text .= ($text ? ', ' : '') - . $this->plugin->txt('include_writer_notes') . ' ' - . $this->plugin->txt($inclusion == CorrectorSummary::INCLUDE_INFO ? 'include_suffix_info' : 'include_suffix_relevant'); - } if (empty($text)) { $text = $this->plugin->txt('include_nothing'); } diff --git a/classes/Task/class.EditorSettingsGUI.php b/classes/Task/class.EditorSettingsGUI.php index 93a5cd58..9fae4c60 100644 --- a/classes/Task/class.EditorSettingsGUI.php +++ b/classes/Task/class.EditorSettingsGUI.php @@ -40,14 +40,18 @@ public function executeCommand() protected function editSettings() { $task_repo = $this->localDI->getTaskRepo(); + $essay_repo = $this->localDI->getEssayRepo(); - $editorSettings = $task_repo->getEditorSettingsById($this->object->getId()); + $editorSettings = $task_repo->getEditorSettingsById($this->object->getId()); + $pdfSettings = $task_repo->getPdfSettingsById($this->object->getId()); + $hasComments = $essay_repo->hasCorrectorCommentsByTaskId($this->object->getId()); $factory = $this->uiFactory->input()->field(); $sections = []; - // Object + // Editor + $fields = []; $fields['headline_scheme'] = $factory->select($this->plugin->txt('headline_scheme'), [ @@ -87,6 +91,80 @@ protected function editSettings() $sections['editor'] = $factory->section($fields, $this->plugin->txt('editor_settings')); + // Processing + + $fields = []; + + $fields['add_paragraph_numbers'] = $factory->checkbox( + $this->plugin->txt('add_paragraph_numbers'), + $this->plugin->txt('add_paragraph_numbers_info')) + ->withDisabled($hasComments) + ->withValue($editorSettings->getAddParagraphNumbers()); + + $fields['add_correction_margin'] = $factory->optionalGroup([ + 'left_correction_margin' => $factory->numeric($this->plugin->txt('left_correction_margin')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withRequired(true) + ->withDisabled($hasComments) + ->withValue($editorSettings->getLeftCorrectionMargin()), + 'right_correction_margin' => $factory->numeric($this->plugin->txt('right_correction_margin')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withRequired(true) + ->withDisabled($hasComments) + ->withValue($editorSettings->getRightCorrectionMargin()), + ], + $this->plugin->txt('add_correction_margin'), + $this->plugin->txt('add_correction_margin_info'), + )->withDisabled($hasComments); + // strange but effective + if (!$editorSettings->getAddCorrectionMargin()) { + $fields['add_correction_margin'] = $fields['add_correction_margin']->withValue(null); + } + + $sections['processing'] = $factory->section($fields, + $this->plugin->txt('processing_settings'), + $this->plugin->txt('processing_settings_info') + ); + + // PDF generation + + $fields = []; + + $fields['add_header'] = $factory->checkbox($this->plugin->txt('pdf_add_header'), $this->plugin->txt('pdf_add_header_info')) + ->withValue($pdfSettings->getAddHeader()); + + $fields['add_footer'] = $factory->checkbox($this->plugin->txt('pdf_add_footer'), $this->plugin->txt('pdf_add_footer_info')) + ->withValue($pdfSettings->getAddFooter()); + + $fields['top_margin'] = $factory->numeric($this->plugin->txt('pdf_top_margin'), $this->plugin->txt('pdf_top_margin_info')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withAdditionalTransformation($this->refinery->int()->isGreaterThan(4)) + ->withRequired(true) + ->withValue($pdfSettings->getTopMargin()); + + $fields['bottom_margin'] = $factory->numeric($this->plugin->txt('pdf_bottom_margin'), $this->plugin->txt('pdf_bottom_margin_info')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withAdditionalTransformation($this->refinery->int()->isGreaterThan(4)) + ->withRequired(true) + ->withValue($pdfSettings->getBottomMargin()); + + $fields['left_margin'] = $factory->numeric($this->plugin->txt('pdf_left_margin'), $this->plugin->txt('pdf_left_margin_info')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withAdditionalTransformation($this->refinery->int()->isGreaterThan(4)) + ->withRequired(true) + ->withValue($pdfSettings->getLeftMargin()); + + $fields['right_margin'] = $factory->numeric($this->plugin->txt('pdf_right_margin'), $this->plugin->txt('pdf_right_margin_info')) + ->withAdditionalTransformation($this->refinery->to()->int()) + ->withAdditionalTransformation($this->refinery->int()->isGreaterThan(4)) + ->withRequired(true) + ->withValue($pdfSettings->getRightMargin()); + + $sections['pdf'] = $factory->section($fields, + $this->plugin->txt('pdf_settings'), + $this->plugin->txt('pdf_settings_info') + ); + $form = $this->uiFactory->input()->container()->form()->standard($this->ctrl->getFormAction($this), $sections); // apply inputs @@ -100,8 +178,28 @@ protected function editSettings() $editorSettings->setHeadlineScheme($data['editor']['headline_scheme']); $editorSettings->setFormattingOptions($data['editor']['formatting_options']); $editorSettings->setNoticeBoards((int) $data['editor']['notice_boards']); - $editorSettings->setCopyAllowed($data['editor']['copy_allowed']); - $task_repo->save($editorSettings); + $editorSettings->setCopyAllowed((bool) $data['editor']['copy_allowed']); + + if (!$hasComments) { + $editorSettings->setAddParagraphNumbers((bool) $data['processing']['add_paragraph_numbers']); + if (isset($data['processing']['add_correction_margin']) && is_array($data['processing']['add_correction_margin'])) { + $editorSettings->setAddCorrectionMargin(true); + $editorSettings->setLeftCorrectionMargin((int) $data['processing']['add_correction_margin']['left_correction_margin']); + $editorSettings->setRightCorrectionMargin((int) $data['processing']['add_correction_margin']['right_correction_margin']); + } + else { + $editorSettings->setAddCorrectionMargin(false); + } + } + $task_repo->save($editorSettings); + + $pdfSettings->setAddHeader((bool) $data['pdf']['add_header']); + $pdfSettings->setAddFooter((bool) $data['pdf']['add_footer']); + $pdfSettings->setTopMargin((int) $data['pdf']['top_margin']); + $pdfSettings->setBottomMargin((int) $data['pdf']['bottom_margin']); + $pdfSettings->setLeftMargin((int) $data['pdf']['left_margin']); + $pdfSettings->setRightMargin((int) $data['pdf']['right_margin']); + $task_repo->save($pdfSettings); ilUtil::sendSuccess($this->lng->txt("settings_saved"), true); $this->ctrl->redirect($this, "editSettings"); diff --git a/classes/Writer/class.WriterContext.php b/classes/Writer/class.WriterContext.php index 6650cc28..c50e0267 100644 --- a/classes/Writer/class.WriterContext.php +++ b/classes/Writer/class.WriterContext.php @@ -3,7 +3,7 @@ namespace ILIAS\Plugin\LongEssayAssessment\Writer; use Edutiek\LongEssayAssessmentService\Data\Alert; -use Edutiek\LongEssayAssessmentService\Data\WritingSettings; +use Edutiek\LongEssayAssessmentService\Data\PageData; use Edutiek\LongEssayAssessmentService\Data\WritingStep; use Edutiek\LongEssayAssessmentService\Data\WritingTask; use Edutiek\LongEssayAssessmentService\Writer\Context; @@ -118,6 +118,32 @@ public function getWrittenEssay(): WrittenEssay ); } + + /** + * @inheritDoc + */ + public function getPagesOfWriter(): array + { + $essay_repo = $this->localDI->getEssayRepo(); + + $pages = []; + if (!empty($repoEssay = $essay_repo->getEssayByWriterIdAndTaskId($this->getRepoWriter()->getId(), $this->getRepoWriter()->getTaskId())) + ) { + foreach ($essay_repo->getEssayImagesByEssayID($repoEssay->getId()) as $repoImage) { + $pages[] = new PageData( + (string) $repoImage->getId(), + $repoImage->getPageNo(), + $repoImage->getWidth(), + $repoImage->getHeight(), + null, + null + ); + } + } + return $pages; + } + + /** * @inheritDoc */ diff --git a/classes/Writer/class.WriterStartGUI.php b/classes/Writer/class.WriterStartGUI.php index bfdbd6fa..01ff241f 100644 --- a/classes/Writer/class.WriterStartGUI.php +++ b/classes/Writer/class.WriterStartGUI.php @@ -339,17 +339,11 @@ protected function startWritingReview() protected function downloadWriterPdf() { if ($this->object->canViewWriterScreen()) { - - $context = new WriterContext(); - $context->init((string) $this->dic->user()->getId(), (string) $this->object->getRefId()); + $service = $this->localDI->getWriterAdminService($this->object->getId()); $repoWriter = $this->localDI->getWriterRepo()->getWriterByUserIdAndTaskId($this->dic->user()->getId(), $this->object->getId()); - $service = new Service($context); - -// $filename = 'task' . $this->object->getId() . '_user' . $this->dic->user()->getId(). '.html'; -// ilUtil::deliverData($service->getProcessedTextAsHtml(), $filename, 'text/html'); $filename = 'task' . $this->object->getId() . '_writer' . $repoWriter->getId(). '-writing.pdf'; - ilUtil::deliverData($service->getProcessedTextAsPdf(), $filename, 'application/pdf'); + ilUtil::deliverData($service->getWritingAsPdf($this->object, $repoWriter), $filename, 'application/pdf'); } else { $this->raisePermissionError(); @@ -357,8 +351,8 @@ protected function downloadWriterPdf() } /** - * Download a generated pdf from the processed written text - */ + * Download a generated pdf from the processed written text + */ protected function downloadCorrectedPdf() { if ($this->object->canReviewCorrectedEssay()) { diff --git a/classes/WriterAdmin/class.WriterAdminGUI.php b/classes/WriterAdmin/class.WriterAdminGUI.php index 852bdbaa..04059243 100644 --- a/classes/WriterAdmin/class.WriterAdminGUI.php +++ b/classes/WriterAdmin/class.WriterAdminGUI.php @@ -83,6 +83,7 @@ public function executeCommand() */ protected function showStartPage() { + $this->addContentCss(); $this->toolbar->setFormAction($this->ctrl->getFormAction($this)); \ilRepositorySearchGUI::fillAutoCompleteToolbar( @@ -721,13 +722,14 @@ protected function getEssaysFromWriterIds(): array protected function showEssay() { $essays = $this->getEssaysFromWriterIds(); - $value = count($essays) > 0 && ($essay = array_pop($essays)) !== null? $essay->getWrittenText() : null; + $value = count($essays) > 0 && ($essay = array_pop($essays)) !== null? $essay->getWrittenText() : ''; + $value = $this->displayContent($this->localDI->getDataService($this->object->getId())->cleanupRichText($value)); $this->ctrl->saveParameter($this, "writer_id"); $link = $this->ctrl->getFormAction($this, "showEssay", "", true); $sight_modal = $this->uiFactory->modal()->roundtrip($this->plugin->txt("submission"), - $this->uiFactory->legacy($value ? $this->localDI->getDataService($this->object->getId())->cleanupRichText($value): "") + $this->uiFactory->legacy($value) ); $reload_button = $this->uiFactory->button()->standard($this->lng->txt("refresh"), "") ->withLoadingAnimationOnClick(true) @@ -811,15 +813,18 @@ protected function changeTextToPdfMultiConfirmation() } if(empty($items)) { - ilUtil::sendFailure($this->plugin->txt("change_text_to_pdf_none_possible"), true); - $this->ctrl->redirect($this, "showStartPage"); + $change_modal = $this->uiFactory->modal()->roundtrip( + $this->plugin->txt("change_text_to_pdf"), + $this->uiFactory->legacy($this->plugin->txt("change_text_to_pdf_none_possible")), + ); + } + else { + $change_modal = $this->uiFactory->modal()->interruptive( + $this->plugin->txt("change_text_to_pdf"), + $this->plugin->txt("change_text_to_pdf_confirmation"), + $this->ctrl->getFormAction($this, "changeTextToPdf") + )->withAffectedItems($items)->withActionButtonLabel('change'); } - - $change_modal = $this->uiFactory->modal()->interruptive( - $this->plugin->txt("change_text_to_pdf"), - $this->plugin->txt("change_text_to_pdf_confirmation"), - $this->ctrl->getFormAction($this, "changeTextToPdf") - )->withAffectedItems($items)->withActionButtonLabel('change'); echo($this->renderer->renderAsync($change_modal)); exit(); @@ -860,7 +865,7 @@ public function changeTextToPdf() { $context = new WriterContext(); $context->init((string) $writer->getUserId(), (string) $this->object->getRefId()); - $service->createPdfFromText($essay, $context); + $service->createPdfFromText($this->object, $essay, $writer); $service->purgeCorrectorComments($essay); } } @@ -871,6 +876,7 @@ public function changeTextToPdf() { public function uploadPDFVersion() { + $essay_repo = $this->localDI->getEssayRepo(); $writer_repo = $this->localDI->getWriterRepo(); $task_repo = $this->localDI->getTaskRepo(); $task_id = $this->object->getId(); @@ -911,10 +917,7 @@ public function uploadPDFVersion() } $service->handlePDFVersionInput($essay, $file_id); - - $context = new WriterContext(); - $context->init((string) $writer->getUserId(), (string) $this->object->getRefId()); - $service->createEssayImages($essay, $context); + $service->createEssayImages($this->object, $essay, $writer); $service->purgeCorrectorComments($essay); $this->ctrl->redirect($this); @@ -949,19 +952,20 @@ public function uploadPDFVersion() : $this->plugin->txt("pdf_version_upload"), $form)->withCard($user_info) ]; - if($essay->getEditStarted()){ - - if($essay->getWritingAuthorized() !== null - && $essay->getWritingAuthorizedBy() === $writer->getUserId() - && $essay->getPdfVersion() === null) - { + if($essay->getEditStarted()) { + if ($essay->getPdfVersion() !== null) { + if ($service->hasCorrectorComments($essay)) { + ilUtil::sendQuestion($this->plugin->txt("pdf_version_info_already_uploaded")); + } + } elseif($essay->getWritingAuthorized() !== null && $essay->getWritingAuthorizedBy() === $writer->getUserId()) { ilUtil::sendQuestion($this->plugin->txt("pdf_version_warning_authorized_essay")); - }else if($essay->getPdfVersion() === null){ - ilUtil::sendInfo($this->plugin->txt("pdf_version_info_started_essay")); + }else { + ilUtil::sendQuestion($this->plugin->txt("pdf_version_info_started_essay")); } - - $subs[] = $this->uiFactory->panel()->sub($this->plugin->txt("writing"), - $this->uiFactory->legacy((string) $essay->getWrittenText()) + + $this->addContentCss(); + $subs[] = $this->uiFactory->panel()->sub($this->plugin->txt("pdf_version_header_writing"), + $this->uiFactory->legacy($this->displayContent($this->localDI->getDataService($task_id)->cleanupRichText($essay->getWrittenText()))) ); } diff --git a/classes/WriterAdmin/class.WriterAdminService.php b/classes/WriterAdmin/class.WriterAdminService.php index 29d170b5..766f4d7e 100644 --- a/classes/WriterAdmin/class.WriterAdminService.php +++ b/classes/WriterAdmin/class.WriterAdminService.php @@ -18,6 +18,7 @@ use ILIAS\Plugin\LongEssayAssessment\Writer\WriterContext; use ILIAS\Filesystem\Stream\Streams; use ILIAS\Plugin\LongEssayAssessment\Data\Essay\EssayImage; +use ilObjLongEssayAssessment; class WriterAdminService extends BaseService { @@ -88,6 +89,23 @@ public function getOrCreateEssayForWriter(Writer $writer) : Essay return $essay; } + /** + * Get the writing of an essay as PDF string + */ + public function getWritingAsPdf(ilObjLongEssayAssessment $object, Writer $repoWriter, bool $anonymous = false, bool $rawContent = false) : string + { + $context = new WriterContext(); + $context->init((string) $repoWriter->getUserId(), (string) $object->getRefId()); + + $writingTask = $context->getWritingTask(); + if ($anonymous) { + $writingTask = $writingTask->withWriterName($repoWriter->getPseudonym()); + } + $writtenEssay = $context->getWrittenEssay(); + + $service = new Service($context); + return $service->getWritingAsPdf($writingTask, $writtenEssay, $rawContent); + } public function createLogExport() { @@ -386,6 +404,12 @@ public function handlePDFVersionInput(Essay $essay, ?string $new_file_id){ } $this->essayRepo->save($essay); } + + public function hasCorrectorComments(Essay $essay) : bool + { + $essay_repo = LongEssayAssessmentDI::getInstance()->getEssayRepo(); + return !empty($essay_repo->getCorrectorCommentsByEssayIdAndCorrectorId($essay->getId(), null)); + } public function purgeCorrectorComments(Essay $essay) { @@ -393,29 +417,27 @@ public function purgeCorrectorComments(Essay $essay) $essay_repo->deleteCorrectorCommentByEssayId($essay->getId()); } - public function createPdfFromText(Essay $essay, WriterContext $context) + public function createPdfFromText(ilObjLongEssayAssessment $object, Essay $essay, Writer $writer) { $essay_repo = LongEssayAssessmentDI::getInstance()->getEssayRepo(); - $service = new Service($context); if (empty($essay->getPdfVersion()) && !empty($essay->getWrittenText())) { - $content = $service->getProcessedTextAsPlainPdf(); + $content = $this->getWritingAsPdf($object, $writer, true, true); $stream = Streams::ofString($content); $file_id = $this->dic->resourceStorage()->manage()->stream($stream, new PDFVersionResourceStakeholder(), $this->plugin->txt('pdf_from_text')); $essay->setPdfVersion((string) $file_id); $essay_repo->save($essay); $this->authorizeWriting($essay, $this->dic->user()->getId()); - // test is put into the PDF, so it does not need to be added to the images - $this->createEssayImages($essay, $context, false); + // text is put into the created PDF, so it does not need to be added to the images + $this->createEssayImages($object, $essay, $writer, false); } } - public function createEssayImages(Essay $essay, WriterContext $context, bool $with_text = true) + public function createEssayImages(ilObjLongEssayAssessment $object, Essay $essay, Writer $writer, bool $with_text = true) { $essay_repo = LongEssayAssessmentDI::getInstance()->getEssayRepo(); - $service = new Service($context); - + $this->removeEssayImages($essay->getId()); $pdfs = []; @@ -423,7 +445,7 @@ public function createEssayImages(Essay $essay, WriterContext $context, bool $wi if ($with_text && !empty($essay->getWrittenText())) { $fs = $this->dic->filesystem()->temp(); - $fs->put('xlas/processed_text.pdf', $service->getProcessedTextAsPlainPdf()); + $fs->put('xlas/processed_text.pdf', $this->getWritingAsPdf($object, $writer,true, true)); $pdfs[] = $fs->readStream('xlas/processed_text.pdf')->detach(); } @@ -434,6 +456,9 @@ public function createEssayImages(Essay $essay, WriterContext $context, bool $wi } if (!empty($pdfs)) { + $context = new WriterContext(); + $context->init((string) $writer->getUserId(), (string) $object->getRefId()); + $service = new Service($context); $images = $service->createPageImagesFromPdfs($pdfs); $page = 1; diff --git a/classes/class.BaseGUI.php b/classes/class.BaseGUI.php index 98c3f7a5..b41fe14b 100644 --- a/classes/class.BaseGUI.php +++ b/classes/class.BaseGUI.php @@ -5,6 +5,7 @@ use ILIAS\DI\Container; use ILIAS\Plugin\LongEssayAssessment\Data\DataService; +use ILIAS\Plugin\LongEssayAssessment\Data\Task\EditorSettings; use ILIAS\UI\Factory; use ILIAS\UI\Renderer; use Psr\Http\Message\RequestInterface; @@ -135,4 +136,41 @@ public function displayText(?string $html) : string { return '
' . $html . '
'; } + + /** + * Display an essay content + */ + public function displayContent(?string $html) : string + { + if (!empty($settings = $this->localDI->getTaskRepo()->getEditorSettingsById($this->object->getId()))) { + switch ($settings->getHeadlineScheme()) { + case EditorSettings::HEADLINE_SCHEME_EDUTIEK: + $headline_class = "headlines-edutiek"; + break; + case EditorSettings::HEADLINE_SCHEME_NUMERIC: + $headline_class = "headlines-numeric"; + break; + } + } + return '
' . $html . '
'; + } + + /** + * Add the css for displaying essay content + */ + public function addContentCss() : void + { + $this->tpl->addCss($this->plugin->getDirectory() .'/templates/css/content.css'); + + if (!empty($settings = $this->localDI->getTaskRepo()->getEditorSettingsById($this->object->getId()))) { + switch ($settings->getHeadlineScheme()) { + case EditorSettings::HEADLINE_SCHEME_EDUTIEK: + $this->tpl->addCss($this->plugin->getDirectory() .'/templates/css/headlines-edutiek.css'); + break; + case EditorSettings::HEADLINE_SCHEME_NUMERIC: + $this->tpl->addCss($this->plugin->getDirectory() .'/templates/css/headlines-numeric.css'); + break; + } + } + } } \ No newline at end of file diff --git a/classes/class.ServiceContext.php b/classes/class.ServiceContext.php index 253974cf..faff1514 100644 --- a/classes/class.ServiceContext.php +++ b/classes/class.ServiceContext.php @@ -18,6 +18,7 @@ use ilSession; use Edutiek\LongEssayAssessmentService\Data\PageImage; use Edutiek\LongEssayAssessmentService\Data\WritingSettings; +use Edutiek\LongEssayAssessmentService\Data\PdfSettings; abstract class ServiceContext implements BaseContext { @@ -393,20 +394,43 @@ public function getPageImage(string $key): ?PageImage public function getWritingSettings(): WritingSettings { $repoSettings = $this->localDI->getTaskRepo()->getEditorSettingsById($this->task->getTaskId()); + return new WritingSettings( $repoSettings->getHeadlineScheme(), $repoSettings->getFormattingOptions(), $repoSettings->getNoticeBoards(), $repoSettings->isCopyAllowed(), $this->plugin->getConfig()->getPrimaryColor(), - $this->plugin->getConfig()->getPrimaryTextColor() + $this->plugin->getConfig()->getPrimaryTextColor(), + $repoSettings->getAddParagraphNumbers(), + $repoSettings->getAddCorrectionMargin(), + $repoSettings->getLeftCorrectionMargin(), + $repoSettings->getRightCorrectionMargin() ); } + /** + * @inheritDoc + */ + public function getPdfSettings(): PdfSettings + { + $repoSettings = $this->localDI->getTaskRepo()->getPdfSettingsById($this->task->getTaskId()); + + return new PdfSettings( + $repoSettings->getAddHeader(), + $repoSettings->getAddFooter(), + $repoSettings->getTopMargin(), + $repoSettings->getBottomMargin(), + $repoSettings->getLeftMargin(), + $repoSettings->getRightMargin() + ); + } + + /** * Get the writing task of a certain writer - * This is individual because if writing end and writing exclusion - * (not needed by interface, but public because needed by CorrectorAdminService) + * This is specific for a writer because of his/her writing end and writing exclusion + * (not needed by interface, but public because needed by WriterAdminService and CorrectorAdminService) */ public function getWritingTaskByWriterId(int $writer_id) : WritingTask { diff --git a/classes/class.ilObjLongEssayAssessment.php b/classes/class.ilObjLongEssayAssessment.php index 98373037..8bf1d541 100644 --- a/classes/class.ilObjLongEssayAssessment.php +++ b/classes/class.ilObjLongEssayAssessment.php @@ -82,16 +82,13 @@ protected function doCreate() $object_repo = $di->getObjectRepo(); $task_repo = $di->getTaskRepo(); - $new_obj_settings = new ObjectSettings($this->getId()); - $new_task_settings = new TaskSettings($this->getId()); - $new_editor_settings = new EditorSettings($this->getId()); $new_correction_settings = (new CorrectionSettings($this->getId())) ->setPositiveRating($this->plugin->txt("comment_rating_positive_default")) ->setNegativeRating($this->plugin->txt("comment_rating_negative_default")); - $object_repo->save($new_obj_settings); - $task_repo->createTask($new_task_settings, $new_editor_settings, $new_correction_settings); - $this->objectSettings = $new_obj_settings; - $this->taskSettings = $new_task_settings; + $task_repo->save($new_correction_settings); + + $this->objectSettings = $object_repo->getObjectSettingsById($this->getId()); + $this->taskSettings = $task_repo->getTaskSettingsById($this->getId()); } /** diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 84ae0385..995a33a6 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -6,23 +6,23 @@ // -obj_xlas#:#Langtext-Aufgabe (Pilot) -objs_xlas#:#Langtext-Aufgaben (Pilot) +obj_xlas#:#Langtext-Aufgabe +objs_xlas#:#Langtext-Aufgaben obj_xlas_duplicate#:#Kopieren -objs_xlas_duplicate#:#Kopiere Langtext-Aufgabe (Pilot) -obj_xlas_select#:#Wähle Langtext-Aufgabe (Pilot) -xlas_add#:#Langtext-Aufgabe hinzufügen (Pilot) -xlas_new#:#Neue Langtext-Aufgabe (Pilot) -xlas_read#:#Lesezugriff auf Langtext-Aufgabe (Pilot) -xlas_write#:#Langtext-Aufgabe bearbeiten (Pilot) -xlas_delete#:#Langtext-Aufgabe löschen (Pilot) -xlas_visible#:#Langtext-Aufgabe ist sichtbar (Pilot) +objs_xlas_duplicate#:#Kopiere Langtext-Aufgabe +obj_xlas_select#:#Wähle Langtext-Aufgabe +xlas_add#:#Langtext-Aufgabe hinzufügen +xlas_new#:#Neue Langtext-Aufgabe +xlas_read#:#Lesezugriff auf Langtext-Aufgabe +xlas_write#:#Langtext-Aufgabe bearbeiten +xlas_delete#:#Langtext-Aufgabe löschen +xlas_visible#:#Langtext-Aufgabe ist sichtbar xlas_edit_permission#:#Zugriffsrecht bearbeiten -crs_create_xlas#:#Erstelle Langtext-Aufgabe (Pilot) -grp_create_xlas#:#Erstelle Langtext-Aufgabe (Pilot) -fold_create_xlas#:#Erstelle Langtext-Aufgabe (Pilot) -root_create_xlas#:#Erstelle Langtext-Aufgabe (Pilot) -cat_create_xlas#:#Erstelle Langtext-Aufgabe (Pilot) +crs_create_xlas#:#Erstelle Langtext-Aufgabe +grp_create_xlas#:#Erstelle Langtext-Aufgabe +fold_create_xlas#:#Erstelle Langtext-Aufgabe +root_create_xlas#:#Erstelle Langtext-Aufgabe +cat_create_xlas#:#Erstelle Langtext-Aufgabe maintain_task#:#Aufgabenstellung verwalten maintain_writers#:#Durchführung verwalten maintain_correctors#:#Korrektur verwalten @@ -106,11 +106,11 @@ task_instructions#:#Aufgabenstellung task_instructions_info#:#Die Aufgabenstellung wird mit Start der Bearbeitung angezeigt. task_solution#:#Lösungshinweise task_solution_info#:#Die Lösungshinweise werden erst ab einem festgelegten Zeitpunkt oder am Ende angezeigt. Dieser wird unter „Organisation ► Lösungshinweise verfügbar“ aktiviert und konfiguriert. Zusätzliche Hinweise können als Datei oder Weblink unter "Material" bereitgestellt werden. -editor_settings#:#Einstellungen für den Editor +editor_settings#:#Schreiben der Aufgabe headline_scheme#:#Überschriften-Schema headline_scheme_none#:#Ohne Numerierung headline_scheme_numeric#:#Numeriert: 1 ‧ 1.1 ‧ 1.1.1 -headline_scheme_edutiek#:#Gutachten: A. ‧ I. ‧ 1 ‧ a. ‧ aa. ‧ (1) +headline_scheme_edutiek#:#Gutachten: A. ‧ I. ‧ 1. ‧ a. ‧ aa. ‧ (1) formatting_options#:#Text-Formatierung formatting_options_none#:#Aus formatting_options_none_info#:#Keine Formatatierungs-Optionen @@ -422,12 +422,13 @@ authorize_pdf_version_info#:#Wenn eine PDF-Abgabe hinzugefügt oder geändert wi pdf_version#:#PDF-Abgabe pdf_version_edit#:#PDF-Abgabe editieren pdf_version_upload#:#PDF-Abgabe hochladen +pdf_version_info_already_uploaded#:#Wenn Sie die vorhandene PDF-Abgabe entfernen oder ersetzen, werden die Markierungen und Kommentare bereits begonnener Korrekturen verworfen! pdf_version_info_started_essay#:#Teilnehmer/in hat bereits einen Text angefangen. Sind Sie sich sicher, dass Sie diese Ergebnisse mit dem PDF-Upload ergänzen möchten? Die Markierungen und Kommentare bereits begonnener Korrekturen werden verworfen. pdf_version_warning_authorized_essay#:#Teilnehmer/in hat bereits einen Text abgegeben und autorisiert. Sind Sie sich sicher, dass Sie diese Ergebnisse mit dem PDF-Upload ergänzen möchten? Die Markierungen und Kommentare bereits begonnener Korrekturen werden verworfen. writing_remove_authorize_log_description#:#Die Autorisierung der Abgabe von %s wurde von %s entfernt. pdf_version_download#:#PDF-Abgabe herunterladen download#:#Herunterladen -writing#:#Bearbeitung +pdf_version_header_writing#:#Text aus der Schreiboberfläche (wird hinzugefügt) pdf_version_upload_not_allowed_by_corrections#:#Sie können keine PDF-Version hochladen, da eine Korrektur bereits autorisiert ist. pdf_version_upload_successful_auth#:#Die PDF-Version wurde erfolgreich hochgeladen und autorisiert. pdf_version_upload_successful_removed#:#Die PDF-Version wurde erfolgreich entfernt. @@ -494,4 +495,26 @@ change_text_to_pdf_none_possible#:#Ihre Auswahl enthält keine Abgaben, die umge change_text_to_pdf_success#:#Die ausgewählten Textabgaben wurden in PDF-Abgaben umgewandelt. pdf_from_text#:#Konvertierte Textabgabe info_missing_correctors#:#Es gibt Abgaben zur Korrektur, aber Sie haben noch keine Korrektoren festgelegt. Wechseln Sie zu "Korrektoren", um Benutzer als Korrektoren hinzuzufügen. Anschließend können Sie diese den Abgaben zuweisen. -info_missing_assignments#:#Es fehlen noch Korrektorenzuweisungen. \ No newline at end of file +info_missing_assignments#:#Es fehlen noch Korrektorenzuweisungen. +processing_settings#:#Weiterverarbeitung +processing_settings_info#:#Diese Einstellungen gelten für die Weiterverarbeitungen des geschriebenen Textes zur Korrektur. Da sie die Markierungsfunktion bei der Korrektur beeinflussen, können sie nicht mehr verändert werden, wenn es bereits Korrekuren mit Markierungen gibt. +add_paragraph_numbers#:#Absatznummern generieren +add_paragraph_numbers_info#:#Alle Überschriften, Absätze und Aufzählungen erhalten am Rand eine Nummer. +add_correction_margin#:#Korrekturrand +add_correction_margin_info#:#Wird ein Text zur Korrektur in ein PDF umgewandelt, kann ein Korrekturrand hinzugefügt werden, um dort Markierungen wie Haken zu platzieren. +left_correction_margin#:#Linker Rand (mm) +right_correction_margin#:#Rechter Rand (mm) +pdf_settings#:#PDF-Generierung +pdf_settings_info#:#Diese Einstellungen gelten für PDF-Dateien, die zur Dokumentation der Abgabe und Korrektur generiert werden. +pdf_add_header#:#Kopfzeile +pdf_add_header_info#:#Fügt unter dem oberen Rand eine Kopfzeile mit den folgenden Informationen ein: Aufgabentitel, Name Teilnehmer/in, Bearbeitungszeit +pdf_add_footer#:#Fußzeile +pdf_add_footer_info#:#Fügt über dem unteren Rand eine Fußzeile mit der Seitenummer des abgegebenen Texts ein +pdf_top_margin#:#Oberer Rand (mm) +pdf_top_margin_info#:#Minimum: 5 +pdf_bottom_margin#:#Unterer Rand (mm) +pdf_bottom_margin_info#:#Minimum: 5 +pdf_left_margin#:#Linker Rand (mm) +pdf_left_margin_info#:#Minimum: 5 +pdf_right_margin#:#Rechter Rand (mm) +pdf_right_margin_info#:#Minimum: 5 \ No newline at end of file diff --git a/plugin.php b/plugin.php index f1e94017..7b3b3244 100644 --- a/plugin.php +++ b/plugin.php @@ -5,7 +5,7 @@ $id = 'xlas'; // code version; must be changed for all code changes -$version = '1.1'; +$version = '1.2'; // ilias min and max version; must always reflect the versions that should // run with the plugin $ilias_min_version = '7.25'; diff --git a/sql/dbupdate.php b/sql/dbupdate.php index 6aa9f11c..8a9278ca 100644 --- a/sql/dbupdate.php +++ b/sql/dbupdate.php @@ -1950,6 +1950,90 @@ 'type' => 'integer', 'length' => '4', 'default' => '0' - ]);} - + ]); +} +?> +<#89> +tableColumnExists('xlas_editor_settings', 'add_paragraph_numbers')) { + $ilDB->addTableColumn('xlas_editor_settings', 'add_paragraph_numbers', [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '1' + ]); +} +if (!$ilDB->tableColumnExists('xlas_editor_settings', 'add_correction_margin')) { + $ilDB->addTableColumn('xlas_editor_settings', 'add_correction_margin', [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '0' + ]); +} +if (!$ilDB->tableColumnExists('xlas_editor_settings', 'left_correction_margin')) { + $ilDB->addTableColumn('xlas_editor_settings', 'left_correction_margin', [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '0' + ]); +} +if (!$ilDB->tableColumnExists('xlas_editor_settings', 'right_correction_margin')) { + $ilDB->addTableColumn('xlas_editor_settings', 'right_correction_margin', [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '0' + ]); +} ?> +<#90> +tableExists('xlas_pdf_settings')) { + $ilDB->createTable('xlas_pdf_settings', [ + 'task_id' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4' + ], + 'add_header' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '1' + ], + 'add_footer' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '1' + ], + 'top_margin' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '10' + ], + 'bottom_margin' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '10' + ], + 'left_margin'=> [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '10' + ], + 'right_margin' => [ + 'notnull' => '1', + 'type' => 'integer', + 'length' => '4', + 'default' => '10' + ] + ]); + $ilDB->addPrimaryKey('xlas_pdf_settings', ['task_id']); +} +?> \ No newline at end of file diff --git a/templates/css/content.css b/templates/css/content.css new file mode 100644 index 00000000..08efaf9d --- /dev/null +++ b/templates/css/content.css @@ -0,0 +1,28 @@ +/** + * Style of written contents + */ + +.mce-content-body, .long-essay-content { + font-family: serif; + font-size: 16px; + max-width: 60em; + line-height: 150%; +} +.mce-content-body h1, .long-essay-content h1, +.mce-content-body h2, .long-essay-content h2, +.mce-content-body h3, .long-essay-content h3, +.mce-content-body h4, .long-essay-content h4, +.mce-content-body h5, .long-essay-content h5, +.mce-content-body h6, .long-essay-content h6 { + font-size: 16px; + font-weight: bold; + padding: 0; + margin-top: 0; + margin-bottom: 10px; +} + +.mce-content-body ol, .long-essay-content ol, +.mce-content-body ul, .long-essay-content ul { + margin-left: 10px; + margin-bottom: 20px; +} diff --git a/templates/css/headlines-edutiek.css b/templates/css/headlines-edutiek.css new file mode 100644 index 00000000..a6b3c4a3 --- /dev/null +++ b/templates/css/headlines-edutiek.css @@ -0,0 +1,54 @@ +/** + * Edutiek headline style of written contents + */ + +.mce-content-body .headlines-edutiek { + counter-reset: h1 h2 h3 h4 h5 h6; +} + +.mce-content-body h1, .headlines-edutiek h1 { + counter-increment: h1; + counter-reset: h2 h3 h4 h5 h6; +} +.mce-content-body h2, .headlines-edutiek h2 { + counter-increment: h2; + counter-reset: h3 h4 h5 h6; +} +.mce-content-body h3, .headlines-edutiek h3 { + counter-increment: h3; + counter-reset: h4 h5 h6; +} +.mce-content-body h4, .headlines-edutiek h4 { + counter-increment: h4; + counter-reset: h5 h6; +} +.mce-content-body h5, .headlines-edutiek h5 { + counter-increment: h5; + counter-reset: h6; +} +.mce-content-body h6, .headlines-edutiek h6 { + counter-increment: h6; +} + +.mce-content-body h1:before, .headlines-edutiek h1::before { + content: counter(h1, upper-latin) ". "; +} + +.mce-content-body h2:before, .headlines-edutiek h2::before { + content: counter(h2, upper-roman) ". "; +} + +.mce-content-body h3:before, .headlines-edutiek h3::before { + content: counter(h3, decimal) ". "; +} + +.mce-content-body h4:before, .headlines-edutiek h4::before { + content: counter(h4, lower-latin) ". "; +} + +.mce-content-body h5:before, .headlines-edutiek h5::before { + content: counter(h5, lower-latin) counter(h5, lower-latin) ". "; +} +.mce-content-body h6:before, .headlines-edutiek h6::before { + content: "(" counter(h6, decimal) ") "; +} diff --git a/templates/css/headlines-numeric.css b/templates/css/headlines-numeric.css new file mode 100644 index 00000000..3cc71ece --- /dev/null +++ b/templates/css/headlines-numeric.css @@ -0,0 +1,50 @@ +/** + * Edutiek headline style of written contents + */ + +.mce-content-body .headlines-numeric { + counter-reset: h1 h2 h3 h4 h5 h6; +} + +.mce-content-body h1, .headlines-numeric h1 { + counter-increment: h1; + counter-reset: h2 h3 h4 h5 h6; +} +.mce-content-body h2, .headlines-numeric h2 { + counter-increment: h2; + counter-reset: h3 h4 h5 h6; +} +.mce-content-body h3, .headlines-numeric h3 { + counter-increment: h3; + counter-reset: h4 h5 h6; +} +.mce-content-body h4, .headlines-numeric h4 { + counter-increment: h4; + counter-reset: h5 h6; +} +.mce-content-body h5, .headlines-numeric h5 { + counter-increment: h5; + counter-reset: h6; +} +.mce-content-body h6, .headlines-numeric h6 { + counter-increment: h6; +} + +.mce-content-body h1:before, .headlines-numeric h1::before { + content: counter(h1, decimal) " "; +} +.mce-content-body h2:before, .headlines-numeric h2::before { + content: counter(h1, decimal) "." counter(h2, decimal) " "; +} +.mce-content-body h3:before, .headlines-numeric h3::before { + content: counter(h1, decimal) "." counter(h2, decimal) "." counter(h3, decimal) " "; +} +.mce-content-body h4:before, .headlines-numeric h4::before { + content: counter(h1, decimal) "." counter(h2, decimal) "." counter(h3, decimal) "." counter(h4, decimal) " "; +} +.mce-content-body h5:before, .headlines-numeric h5::before { + content: counter(h1, decimal) "." counter(h2, decimal) "." counter(h3, decimal) "." counter(h4, decimal) "." counter(h5, decimal) " "; +} +.mce-content-body h6:before, .headlines-numeric h6::before { + content: counter(h1, decimal) "." counter(h2, decimal) "." counter(h3, decimal) "." counter(h4, decimal) "." counter(h5, decimal) "." counter(h6, decimal) " "; +}