diff --git a/app/controllers/course/members.php b/app/controllers/course/members.php index 7812771ed6d982f2b5218afc664fe3bfbfe127f2..cdf0641875571ca612d742292352fd6c9ecf6eba 100644 --- a/app/controllers/course/members.php +++ b/app/controllers/course/members.php @@ -1783,6 +1783,15 @@ class Course_MembersController extends AuthenticatedController Icon::create('export') ); + $widget->addLink( + _('Als Word-Datei exportieren'), + URLHelper::getURL('dispatch.php/course/members/export', [ + 'course_id' => $this->course_id, + 'format' => 'docx', + ]), + Icon::create('export') + ); + $widget->addLink( _('Als CSV-Datei exportieren'), URLHelper::getURL('dispatch.php/course/members/export', [ @@ -1793,6 +1802,8 @@ class Course_MembersController extends AuthenticatedController ); if (count($this->awaiting) > 0) { + $widget->addSeparator(); + $widget->addLink( _('Warteliste als Excel-Datei exportieren'), URLHelper::getURL('dispatch.php/course/members/export', [ @@ -1802,6 +1813,17 @@ class Course_MembersController extends AuthenticatedController ]), Icon::create('export') ); + + $widget->addLink( + _('Warteliste als Word-Datei exportieren'), + URLHelper::getURL('dispatch.php/course/members/export', [ + 'course_id' => $this->course_id, + 'format' => 'docx', + 'status' => $this->waiting_type, + ]), + Icon::create('export') + ); + $widget->addLink( _('Warteliste als CSV-Datei exportieren'), URLHelper::getURL('dispatch.php/course/members/export', [ @@ -1862,9 +1884,10 @@ class Course_MembersController extends AuthenticatedController $export_format = Request::get('format'); $status = Request::get('status'); - if ($export_format !== 'csv' && $export_format !== 'xlsx') { + if (!in_array($export_format, ['csv', 'docx', 'xlsx'])) { throw new Exception('Wrong format'); } + $header = [ _('Status'), _('Anrede'), @@ -1893,7 +1916,70 @@ class Course_MembersController extends AuthenticatedController $filename = $filename . ' ' . $this->course_title . '.' . $export_format; - $this->render_spreadsheet($header, $members, $export_format, $filename); + if ($export_format === 'docx') { + $document = new PhpOffice\PhpWord\PhpWord(); + + $section = $document->addSection(); + + $header = $section->addHeader(); + $header->addText($course->getFullName()); + + $footer = $section->addFooter(); + $footer->addText(studip_interpolate( + _('Generiert mit Stud.IP Version %{version}'), + ['version' => StudipVersion::getStudipVersion()] + )); + + $section->addTextBreak(2); + $section->addText(_('Teilnehmendenliste')); + + $table = $section->addTable([ + 'layout' => \PhpOffice\PhpWord\Style\Table::LAYOUT_FIXED, + 'unit' => \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT, + 'width' => 100 * 50, + ]); + + $row = $table->addRow(); + $row->addCell()->addText(_('Name')); + $row->addCell()->addText(_('E-Mail')); + $row->addCell()->addText(_('Anmeldedatum')); + $row->addCell()->addText(_('Matrikelnummer')); + + foreach (['dozent', 'tutor', 'autor', 'user'] as $one) { + $people = array_filter($members, fn($member) => $member['status'] === $one); + if (count($people) === 0) { + continue; + } + + $cell = $table->addRow()->addCell(); + $cell->addText( + get_title_for_status($one, 2, $course->status) + ); + $cell->getStyle()->setGridSpan(4); + + foreach ($people as $person) { + $row = $table->addRow(); + $row->addCell()->addText($person['Vorname'] . ' ' . $person['Nachname']); + $row->addCell()->addText($person['Email']); + $row->addCell()->addText($person['Anmeldedatum']); + $row->addCell()->addText($person['Matrikelnummer'] ?? ''); + } + } + + $filepath = tempnam($GLOBALS['TMP_PATH'], 'docx'); + + $writer = \PhpOffice\PhpWord\IOFactory::createWriter($document); + $writer->save($filepath); + + $this->response->add_header('Cache-Control', 'cache, must-revalidate'); + $this->render_temporary_file( + $filepath, + $filename, + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' + ); + } else { + $this->render_spreadsheet($header, $members, $export_format, $filename); + } } public function toggle_student_mailing_action($state) diff --git a/composer.json b/composer.json index 32617d6c8d70320afb79deef2edce50e8957e63b..4a355a2385887a056eca4dd8f11e3089a4c6c004 100644 --- a/composer.json +++ b/composer.json @@ -125,7 +125,8 @@ "nyholm/psr7-server": "1.1.0", "erusev/parsedown": "1.7.4", "league/oauth2-client": "2.7.0", - "nette/php-generator": "4.1.5" + "nette/php-generator": "4.1.5", + "phpoffice/phpword": "1.3.0" }, "replace": { "symfony/polyfill-php73": "*", diff --git a/composer.lock b/composer.lock index 7ebf09c08e4c90a41fb423c9cafb4e39d4da506c..b52c3103a9457236465cb3db488a8b842e8b1bbf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f64a6bfba9a1758196b21c6394535191", + "content-hash": "9185ac4711a13ab2305911de1f511f6a", "packages": [ { "name": "algo26-matthias/idna-convert", @@ -2978,6 +2978,58 @@ ], "time": "2023-01-12T14:08:11+00:00" }, + { + "name": "phpoffice/math", + "version": "0.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/Math.git", + "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/Math/zipball/fc2eb6d1a61b058d5dac77197059db30ee3c8329", + "reference": "fc2eb6d1a61b058d5dac77197059db30ee3c8329", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xml": "*", + "php": "^7.1|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.88 || ^1.0.0", + "phpunit/phpunit": "^7.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\Math\\": "src/Math/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Progi1984", + "homepage": "https://lefevre.dev" + } + ], + "description": "Math - Manipulate Math Formula", + "homepage": "https://phpoffice.github.io/Math/", + "keywords": [ + "MathML", + "officemathml", + "php" + ], + "support": { + "issues": "https://github.com/PHPOffice/Math/issues", + "source": "https://github.com/PHPOffice/Math/tree/0.2.0" + }, + "time": "2024-08-12T07:30:45+00:00" + }, { "name": "phpoffice/phpspreadsheet", "version": "2.1.0", @@ -3082,6 +3134,115 @@ }, "time": "2024-05-11T04:17:56+00:00" }, + { + "name": "phpoffice/phpword", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PHPWord.git", + "reference": "8392134ce4b5dba65130ba956231a1602b848b7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PHPWord/zipball/8392134ce4b5dba65130ba956231a1602b848b7f", + "reference": "8392134ce4b5dba65130ba956231a1602b848b7f", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-xml": "*", + "php": "^7.1|^8.0", + "phpoffice/math": "^0.2" + }, + "require-dev": { + "dompdf/dompdf": "^2.0", + "ext-gd": "*", + "ext-libxml": "*", + "ext-zip": "*", + "friendsofphp/php-cs-fixer": "^3.3", + "mpdf/mpdf": "^8.1", + "phpmd/phpmd": "^2.13", + "phpstan/phpstan-phpunit": "@stable", + "phpunit/phpunit": ">=7.0", + "symfony/process": "^4.4 || ^5.0", + "tecnickcom/tcpdf": "^6.5" + }, + "suggest": { + "dompdf/dompdf": "Allows writing PDF", + "ext-gd2": "Allows adding images", + "ext-xmlwriter": "Allows writing OOXML and ODF", + "ext-xsl": "Allows applying XSL style sheet to headers, to main document part, and to footers of an OOXML template", + "ext-zip": "Allows writing OOXML and ODF" + }, + "type": "library", + "autoload": { + "psr-4": { + "PhpOffice\\PhpWord\\": "src/PhpWord" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0" + ], + "authors": [ + { + "name": "Mark Baker" + }, + { + "name": "Gabriel Bull", + "email": "me@gabrielbull.com", + "homepage": "http://gabrielbull.com/" + }, + { + "name": "Franck Lefevre", + "homepage": "https://rootslabs.net/blog/" + }, + { + "name": "Ivan Lanin", + "homepage": "http://ivan.lanin.org" + }, + { + "name": "Roman Syroeshko", + "homepage": "http://ru.linkedin.com/pub/roman-syroeshko/34/a53/994/" + }, + { + "name": "Antoine de Troostembergh" + } + ], + "description": "PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)", + "homepage": "https://phpoffice.github.io/PHPWord/", + "keywords": [ + "ISO IEC 29500", + "OOXML", + "Office Open XML", + "OpenDocument", + "OpenXML", + "PhpOffice", + "PhpWord", + "Rich Text Format", + "WordprocessingML", + "doc", + "docx", + "html", + "odf", + "odt", + "office", + "pdf", + "php", + "reader", + "rtf", + "template", + "template processor", + "word", + "writer" + ], + "support": { + "issues": "https://github.com/PHPOffice/PHPWord/issues", + "source": "https://github.com/PHPOffice/PHPWord/tree/1.3.0" + }, + "time": "2024-08-30T18:03:42+00:00" + }, { "name": "phpoption/phpoption", "version": "1.9.2", @@ -8742,7 +8903,8 @@ "ext-pcre": "*", "ext-pdo": "*", "ext-mbstring": "*", - "ext-dom": "*" + "ext-dom": "*", + "ext-iconv": "*" }, "platform-dev": [], "platform-overrides": {