diff --git a/app/controllers/file.php b/app/controllers/file.php index 051f05d8195e6990d30e388a3a4b1c9626ecb9d2..5fbe4393ff44be60aa02f9446c8bf99c13fb6bb6 100644 --- a/app/controllers/file.php +++ b/app/controllers/file.php @@ -492,6 +492,57 @@ class FileController extends AuthenticatedController $this->redirect("oer/mymaterial/edit"); } + public function suggest_oer_action($file_ref_id) + { + $this->file_ref_id = $file_ref_id; + $file_ref = FileRef::find($file_ref_id); + $filetype = $file_ref->getFileType(); + $this->file = $filetype->convertToStandardFile(); + + $this->author = $file_ref->owner->username; + $this->author_fullname = $file_ref->owner->getFullName('no_title'); + $this->link_to_share = URLHelper::getURL("dispatch.php/file/share_oer/" . $file_ref_id); + $this->linktext = _('Klicken Sie hier, um das Material im OER Campus zu veröffentlichen'); + $this->formatted_link = '['. $this->linktext .']' . $this->link_to_share; + $additional_text = htmlReady(Request::get('additional_text')); + + $oer_suggestion_message = sprintf(_("Ihre hochgeladene Datei wurde zur Veröffentlichung im + OER Campus vorgeschlagen:\n\n" + . "Dateiname: %s \n" + . "Beschreibung: %s \n\n" + . "%s \n\n" + . "Zusätzliche Info: \n %s"), + $this->file->getFilename(), + $this->file->getDescription(), + $this->formatted_link, + $additional_text + ); + + if (Request::isPost()) { + CSRFProtection::verifyUnsafeRequest(); + + // send a private message to the file author + $messaging = new messaging(); + + $messaging->insert_message( + $oer_suggestion_message, + $this->author, + '____%system%____', + '', + Request::option('message_id'), + '', + null, + _('Vorschlag zur Veröffentlichung einer Datei im OER Campus') + ); + $this->response->add_header('X-Dialog-Close', '1'); + $this->render_nothing(); + + PageLayout::postSuccess(_('Vorschlag wurde eingereicht.')); + + } + + } + public function edit_urlfile_action($file_ref_id) { $this->file_ref = FileRef::find($file_ref_id); diff --git a/app/controllers/messages.php b/app/controllers/messages.php index 2ec5d324242f1764066b2e99d114618b89fc285d..a8b0788cd9c814a907e1949b2af4943a55d5b6a9 100644 --- a/app/controllers/messages.php +++ b/app/controllers/messages.php @@ -909,6 +909,45 @@ class MessagesController extends AuthenticatedController { $this->redirect('messages/overview'); } + public function sendCwMessage_action($course_id, $element_id, $owner_id) + { + $body = file_get_contents('php://input'); + $data = json_decode($body, true); + $text = $data['text']; + + $author = User::find($owner_id)->username; + + $this->link_to_share = URLHelper::getURL("dispatch.php/course/courseware/?cid=" . $course_id + . "#/structural_element/" . $element_id); + $this->linktext = _('Klicken Sie hier, um zum vorgeschlagenen Courseware-Material zu gelangen'); + $this->formatted_link = '['. $this->linktext .']' . $this->link_to_share; + + $oer_suggestion_message = sprintf(_("Ihr Courseware-Material wurde zur Veröffentlichung im OER Campus vorgeschlagen:\n\n" + . "%s \n\n" + . "Zusätzliche Info: \n %s"), + $this->formatted_link, $text); + + $messaging = new messaging(); + + $messaging->insert_message( + $oer_suggestion_message, + $author, + '____%system%____', + '', + Request::option('message_id'), + '', + null, + _('Vorschlag zur Veröffentlichung von Courseware-Material im OER Campus'), + '', + 'normal', + '', + 0 + ); + + $this->render_nothing(); + + } + public function setupSidebar($action) { $sidebar = Sidebar::get(); diff --git a/app/views/file/suggest_oer.php b/app/views/file/suggest_oer.php new file mode 100644 index 0000000000000000000000000000000000000000..058c967a8f161b0d0f4d53c36683d132807d4a63 --- /dev/null +++ b/app/views/file/suggest_oer.php @@ -0,0 +1,21 @@ +<form action='<?= $controller->url_for('file/suggest_oer/' . $file_ref_id)?>' + class='default' method='POST' data-dialog="reload-on-close"> + <?= CSRFProtection::tokenTag() ?> + + <p><?= sprintf(_('Das folgende Material wird %s zur Veröffentlichung im OER Campus vorgeschlagen:'), $author_fullname)?></p> + <p><?= htmlReady($file->getFilename())?></p> + <label for="additional_text"> + <span class=""> + <?= _("Ihr Vorschlag wird anonym versendet.") ?> + <?= _("Falls gewünscht, können Sie zusätzlich eine Nachricht verfassen:") ?> + </span> + <textarea name = "additional_text" + id = "additional_text" + rows = "3" + ></textarea> + </label> + + <footer data-dialog-button> + <?= Studip\Button::create(_("Material vorschlagen"))?> + </footer> +</form> diff --git a/db/migrations/5.2.2_oer_material_suggestion.php b/db/migrations/5.2.2_oer_material_suggestion.php new file mode 100644 index 0000000000000000000000000000000000000000..77f828790854350fc7bca62d20c9c2d2f4ca1079 --- /dev/null +++ b/db/migrations/5.2.2_oer_material_suggestion.php @@ -0,0 +1,41 @@ +<?php +class OerMaterialSuggestion extends Migration +{ + public function description() + { + return "Adds config option to enable suggestions"; + } + + public function up() + { + $query = "INSERT INTO `config` + SET `field` = :field, + `value` = :value, + `type` = :type, + `range` = :range, + `section` = :section, + `mkdate` = UNIX_TIMESTAMP(), + `chdate` = UNIX_TIMESTAMP(), + `description` = :description"; + $config_statement = DBManager::get()->prepare($query); + + $config_statement->execute([ + ':field' => 'OER_ENABLE_SUGGESTIONS', + ':value' => '1', + ':type' => 'boolean', + ':range' => 'global', + ':section' => 'OERCampus', + ':description' => 'Studierendenvorschläge erlauben?', + ]); + } + + public function down() + { + $query = "DELETE `config`, `config_values` + FROM `config` + LEFT JOIN `config_values` USING (`field`) + WHERE `field` = 'OER_ENABLE_SUGGESTIONS'"; + DBManager::get()->exec($query); + } + +} diff --git a/lib/filesystem/StandardFile.php b/lib/filesystem/StandardFile.php index 0e09090e5a127ae081fdf9e5a0ab873c71bff69b..74e538ae86f5f43320973d39a82c2e55227d274f 100644 --- a/lib/filesystem/StandardFile.php +++ b/lib/filesystem/StandardFile.php @@ -301,6 +301,19 @@ class StandardFile implements FileType, ArrayAccess ['data-dialog' => '1'] ); } + + if (Config::get()->OER_ENABLE_SUGGESTIONS && + ($this->getTermsOfUse()->id === 'SELFMADE_NONPUB' || $this->getTermsOfUse()->id === 'FREE_LICENSE') && + $this->fileref->getAuthorName() != User::findCurrent()->getFullName('no_title_rev') + ) { + $actionMenu->addLink( + URLHelper::getURL('dispatch.php/file/suggest_oer/' . $this->fileref->id), + _('Material für OER Campus vorschlagen'), + Icon::create('oer-campus'), + ['data-dialog' => 'reload-on-close'] + ); + } + if (Context::isCourse() && Feedback::isActivated()) { if (Feedback::hasCreatePerm(Context::getId())) { $actionMenu->addLink( diff --git a/resources/assets/javascripts/bootstrap/oer.js b/resources/assets/javascripts/bootstrap/oer.js index a5ba0b411570dd4101c84fee60fb327fde4fcea2..eff927f47977e3437a73a8992811299b27362335 100644 --- a/resources/assets/javascripts/bootstrap/oer.js +++ b/resources/assets/javascripts/bootstrap/oer.js @@ -51,7 +51,7 @@ STUDIP.domReady(() => { }); -STUDIP.dialogReady(() => { +STUDIP.ready(() => { if ($('.oercampus_editmaterial').length) { STUDIP.Vue.load().then(({createApp}) => { diff --git a/resources/assets/stylesheets/scss/courseware.scss b/resources/assets/stylesheets/scss/courseware.scss index 8829ebdb6d30a1ae4d985cfe70c6166ce8f19ef6..5f96311752cdeacca94425008fa8b9ec511955ad 100755 --- a/resources/assets/stylesheets/scss/courseware.scss +++ b/resources/assets/stylesheets/scss/courseware.scss @@ -1920,6 +1920,9 @@ v i e w w i d g e t .cw-action-widget-trash{ @include background-icon(trash, clickable); } + .cw-action-widget-oer{ + @include background-icon(oer-campus, clickable); + } } .cw-export-widget { .cw-export-widget-export{ diff --git a/resources/vue/components/courseware/CoursewareActionWidget.vue b/resources/vue/components/courseware/CoursewareActionWidget.vue index d95b32bdcda23a6150f0378aa46837240ed0fac7..a49fe5535c8b53a17cc30c39391a3d9cce891b95 100644 --- a/resources/vue/components/courseware/CoursewareActionWidget.vue +++ b/resources/vue/components/courseware/CoursewareActionWidget.vue @@ -35,6 +35,11 @@ <translate>Lesezeichen setzen</translate> </button> </li> + <li v-if="!isOwner" class="cw-action-widget-oer"> + <button @click="suggestOER"> + <translate>Material für OER Campus vorschlagen</translate> + </button> + </li> <li v-if="!isRoot && canEdit" class="cw-action-widget-trash"> <button @click="deleteElement"> <translate>Seite löschen</translate> @@ -94,6 +99,9 @@ export default { }, isTask() { return this.structuralElement?.relationships.task.data !== null; + }, + isOwner() { + return this.structuralElement.relationships.owner.data.id === this.userId; } }, methods: { @@ -102,6 +110,7 @@ export default { showElementAddDialog: 'showElementAddDialog', showElementDeleteDialog: 'showElementDeleteDialog', showElementInfoDialog: 'showElementInfoDialog', + updateShowSuggestOerDialog: 'updateShowSuggestOerDialog', setStructuralElementSortMode: 'setStructuralElementSortMode', companionInfo: 'companionInfo', addBookmark: 'addBookmark', @@ -155,6 +164,9 @@ export default { this.setSelectedToolbarItem('contents'); this.setConsumeMode(true); }, + suggestOER() { + this.updateShowSuggestOerDialog(true); + }, }, }; </script> diff --git a/resources/vue/components/courseware/CoursewareStructuralElement.vue b/resources/vue/components/courseware/CoursewareStructuralElement.vue index 9276ba7dcba5bd243397381e71ccb762aa9a0037..71caff47fcce8ce4e19a4fa1713ee77e979dc0ad 100755 --- a/resources/vue/components/courseware/CoursewareStructuralElement.vue +++ b/resources/vue/components/courseware/CoursewareStructuralElement.vue @@ -58,6 +58,7 @@ @setBookmark="menuAction('setBookmark')" @sortContainers="menuAction('sortContainers')" @pdfExport="menuAction('pdfExport')" + @showSuggest="menuAction('showSuggest')" /> </template> </courseware-ribbon> @@ -478,6 +479,43 @@ </form> </template> </studip-dialog> + <studip-dialog + v-if="showSuggestOerDialog" + height="600" + width="600" + :title="textSuggestOer.title" + :confirmText="textSuggestOer.confirm" + confirmClass="accept" + :closeText="textSuggestOer.close" + closeClass="cancel" + @close="updateShowSuggestOerDialog(false)" + @confirm="sendOerSuggestion" + > + <template v-slot:dialogContent> + <p><translate>Das folgende Courseware-Material wird {{ owner }} + zur Veröffentlichung im OER Campus vorgeschlagen:</translate></p> + <table class="cw-structural-element-info"> + <tr> + <td><translate>Titel</translate>:</td> + <td>{{ structuralElement.attributes.title }}</td> + </tr> + <tr> + <td><translate>Beschreibung</translate>:</td> + <td>{{ structuralElement.attributes.payload.description }}</td> + </tr> + </table> + <form class="default" @submit.prevent=""> + <label> + <translate>Ihr Vorschlag wird anonym versendet. Falls gewünscht, können Sie + zusätzlich eine Nachricht verfassen:</translate> + <textarea + v-model="additionalText" + class="cw-structural-element-description" + /> + </label> + </form> + </template> + </studip-dialog> <studip-dialog v-if="showDeleteDialog" :title="textDelete.title" @@ -514,6 +552,7 @@ import CoursewareRibbon from './CoursewareRibbon.vue'; import CoursewareTabs from './CoursewareTabs.vue'; import CoursewareTab from './CoursewareTab.vue'; import CoursewareExport from '@/vue/mixins/courseware/export.js'; +import CoursewareOerMessage from '@/vue/mixins/courseware/oermessage.js'; import { FocusTrap } from 'focus-trap-vue'; import IsoDate from './IsoDate.vue'; import StudipDialog from '../StudipDialog.vue'; @@ -542,7 +581,7 @@ export default { }, props: ['canVisit', 'orderedStructuralElements', 'structuralElement'], - mixins: [CoursewareExport], + mixins: [CoursewareExport, CoursewareOerMessage], data() { return { @@ -593,6 +632,7 @@ export default { }, errorEmptyChapterName: false, consumModeTrap: false, + additionalText: '', }; }, @@ -616,6 +656,7 @@ export default { showInfoDialog: 'showStructuralElementInfoDialog', showDeleteDialog: 'showStructuralElementDeleteDialog', showOerDialog: 'showStructuralElementOerDialog', + showSuggestOerDialog: 'showSuggestOerDialog', oerEnabled: 'oerEnabled', oerTitle: 'oerTitle', licenses: 'licenses', @@ -639,6 +680,14 @@ export default { }; }, + textSuggestOer() { + return { + title: this.$gettext('Material für OER Campus vorschlagen'), + confirm: this.$gettext('Material vorschlagen'), + close: this.$gettext('Schließen'), + }; + }, + inCourse() { return this.$store.getters.context.type === 'courses'; }, @@ -1068,6 +1117,7 @@ export default { showElementInfoDialog: 'showElementInfoDialog', showElementDeleteDialog: 'showElementDeleteDialog', showElementOerDialog: 'showElementOerDialog', + updateShowSuggestOerDialog: 'updateShowSuggestOerDialog', updateContainer: 'updateContainer', setStructuralElementSortMode: 'setStructuralElementSortMode', sortContainersInStructualElements: 'sortContainersInStructualElements', @@ -1118,6 +1168,9 @@ export default { case 'oerCurrentElement': this.showElementOerDialog(true); break; + case 'showSuggest': + this.updateShowSuggestOerDialog(true); + break; case 'setBookmark': this.setBookmark(); break; @@ -1312,6 +1365,10 @@ export default { updateWriteApproval(approval) { this.currentElement.attributes['write-approval'] = approval; }, + sendOerSuggestion() { + this.suggestViaAction(this.currentElement, this.additionalText); + this.updateShowSuggestOerDialog(false); + } }, created() { this.pluginManager.registerComponentsLocally(this); diff --git a/resources/vue/mixins/courseware/oermessage.js b/resources/vue/mixins/courseware/oermessage.js new file mode 100644 index 0000000000000000000000000000000000000000..ff50d1b0c714f35560e9df4e6d7f490bb8413a62 --- /dev/null +++ b/resources/vue/mixins/courseware/oermessage.js @@ -0,0 +1,24 @@ +import axios from 'axios'; + +export default { + methods: { + suggestViaAction(element, text) { + let owner = element.relationships.owner.data.id; + let cid = element.relationships.course.data.id; + let elementid = element.id; + + axios({ + method: 'post', + url: STUDIP.URLHelper.getURL('dispatch.php/messages/sendCwMessage/' + cid + '/' + elementid + '/' + owner), + data: { + text: text, + }, + + }).then( () => { + this.companionInfo({ info: this.$gettextInterpolate('Der Vorschlag wurde verschickt.') }); + }); + + } + }, + +}; diff --git a/resources/vue/store/courseware/courseware.module.js b/resources/vue/store/courseware/courseware.module.js index b51276d5cc7c501cce7980078ff51efa5a19df54..bf07efcc320cc53eefb2c59d84cb21263758944b 100755 --- a/resources/vue/store/courseware/courseware.module.js +++ b/resources/vue/store/courseware/courseware.module.js @@ -37,6 +37,8 @@ const getDefaultState = () => { showStructuralElementDeleteDialog: false, showStructuralElementOerDialog: false, + showSuggestOerDialog: false, + structuralElementSortMode: false, importFilesState: '', @@ -169,6 +171,9 @@ const getters = { showOverviewElementAddDialog(state) { return state.showOverviewElementAddDialog; }, + showSuggestOerDialog(state) { + return state.showSuggestOerDialog; + }, structuralElementSortMode(state) { return state.structuralElementSortMode; }, @@ -798,6 +803,10 @@ export const actions = { context.commit('setShowStructuralElementOerDialog', bool); }, + updateShowSuggestOerDialog(context, bool) { + context.commit('setShowSuggestOerDialog', bool); + }, + showElementDeleteDialog(context, bool) { context.commit('setShowStructuralElementDeleteDialog', bool); }, @@ -1266,6 +1275,10 @@ export const mutations = { state.showStructuralElementInfoDialog = showInfo; }, + setShowSuggestOerDialog(state, showSuggest) { + state.showSuggestOerDialog = showSuggest; + }, + setShowStructuralElementOerDialog(state, showOer) { state.showStructuralElementOerDialog = showOer; },