2424use moodle_exception ;
2525use moodle_url ;
2626use qtype_questionpy \constants ;
27+ use qtype_questionpy \local \form \context \render_context ;
2728use qtype_questionpy \local \form \elements \file_upload_element ;
2829use qtype_questionpy \local \form \elements \file_upload_options ;
2930use qtype_questionpy_question ;
@@ -47,61 +48,77 @@ class options_file_service implements handles_qpy_url_type {
4748 * @param int $contextid Context id of the question (NOT the draft area).
4849 * @param int $questionid
4950 * @param int $userid User whose draft area should be used, which is most likely the current user.
50- * @param int $draftitemid
51+ * @param int[] $draftitemids
5152 * @throws file_exception
5253 * @throws stored_file_creation_exception
5354 * @throws coding_exception
55+ * @throws moodle_exception
5456 */
55- public function save_draft_area_files (int $ contextid , int $ questionid , int $ userid , int $ draftitemid ): void {
57+ public function save_draft_area_files (int $ contextid , int $ questionid , int $ userid , array $ draftitemids ): void {
5658 $ fs = get_file_storage ();
59+ $ usercontext = context_user::instance ($ userid );
5760
58- $ existingfiles = $ fs ->get_area_files (
59- $ contextid ,
60- 'qtype_questionpy ' ,
61- constants::FILEAREA_OPTIONS ,
62- $ questionid ,
63- includedirs: false
64- );
65- $ existingfilerefs = array_map (fn ($ file ) => $ file ->get_filename (), $ existingfiles );
61+ // We copy the files in all the separate draft areas to one combined draft area first, so that we can use
62+ // file_save_draft_area_files, which does some magic concerning the file source.
6663
67- // Copy all draft files to the "permanent" file area and collect their metadata.
68- $ draftfiles = $ fs ->get_area_files (context_user::instance ($ userid )->id , 'user ' , 'draft ' , $ draftitemid , includedirs: false );
64+ $ tempcombineddraftarea = file_get_unused_draft_itemid ();
65+
66+ $ draftfiles = $ fs ->get_area_files ($ usercontext ->id , 'user ' , 'draft ' , $ draftitemids , includedirs: false );
6967 foreach ($ draftfiles as $ draftfile ) {
7068 $ fileref = qpy_file_ref::from_stored_file ($ draftfile );
7169 // If the file ref already exists, the user just didn't modify/remove the file.
72- if (!in_array (strval ($ fileref ), $ existingfilerefs )) {
73- $ fs ->create_file_from_storedfile ([
74- 'component ' => 'qtype_questionpy ' ,
75- 'filearea ' => 'options ' ,
76- 'itemid ' => $ questionid ,
77- 'contextid ' => $ contextid ,
78- 'filepath ' => '/ ' ,
79- 'filename ' => $ fileref ,
80- ], $ draftfile );
81- }
70+ $ fs ->create_file_from_storedfile ([
71+ 'component ' => 'user ' ,
72+ 'filearea ' => 'draft ' ,
73+ 'itemid ' => $ tempcombineddraftarea ,
74+ 'contextid ' => $ usercontext ->id ,
75+ 'filepath ' => '/ ' ,
76+ 'filename ' => $ fileref ,
77+ ], $ draftfile );
8278 }
79+
80+ // Save the combined draft area the question file area.
81+ file_save_draft_area_files (
82+ draftitemid: $ tempcombineddraftarea ,
83+ contextid: $ contextid ,
84+ component: 'qtype_questionpy ' ,
85+ filearea: constants::FILEAREA_OPTIONS ,
86+ itemid: $ questionid ,
87+ );
8388 }
8489
8590 /**
8691 * Populates the given draft area with files listed in `$filemetas` and stored in the permanent question file area.
8792 *
8893 * (The inverse of {@see save_draft_area_files}.)
8994 *
90- * @param int $contextid Context id of the question (NOT the draft area).
91- * @param int $questionid
9295 * @param file_metadata[] $filemetas
9396 * @param int $userid
94- * @param int $draftitemid
97+ * @param int $combineddraftitemid The draft area returned by {@see render_context::prepare_combined_draft_area()}.
98+ * @param int $targetdraftitemid The (new, not prepared before) separate draft area belonging to a single form element.
99+ * @throws coding_exception
95100 * @throws file_exception
96101 * @throws stored_file_creation_exception
97- * @throws coding_exception
98102 */
99- public function prepare_draft_area (int $ contextid , int $ questionid , array $ filemetas , int $ userid , int $ draftitemid ): void {
103+ public function prepare_split_draft_area (
104+ array $ filemetas ,
105+ int $ userid ,
106+ int $ combineddraftitemid ,
107+ int $ targetdraftitemid
108+ ): void {
100109 $ fs = get_file_storage ();
101- $ files = $ fs ->get_area_files ($ contextid , 'qtype_questionpy ' , constants::FILEAREA_OPTIONS , $ questionid , includedirs: false );
110+ $ usercontext = context_user::instance ($ userid );
111+
112+ $ combinedfiles = $ fs ->get_area_files (
113+ $ usercontext ->id ,
114+ 'user ' ,
115+ 'draft ' ,
116+ $ combineddraftitemid ,
117+ includedirs: false
118+ );
102119
103120 foreach ($ filemetas as $ filemetadata ) {
104- $ matchingfiles = array_filter ($ files , fn ($ file ) => $ file ->get_filename () === $ filemetadata ->fileref );
121+ $ matchingfiles = array_filter ($ combinedfiles , fn ($ file ) => $ file ->get_filename () === $ filemetadata ->fileref );
105122 if (!$ matchingfiles ) {
106123 debugging ("Options file ' $ filemetadata ->filename ' with file_ref ' $ filemetadata ->fileref ' could not be found in "
107124 . 'storage. ' );
@@ -120,8 +137,8 @@ public function prepare_draft_area(int $contextid, int $questionid, array $filem
120137 $ fs ->create_file_from_storedfile ([
121138 'component ' => 'user ' ,
122139 'filearea ' => 'draft ' ,
123- 'itemid ' => $ draftitemid ,
124- 'contextid ' => context_user:: instance ( $ userid ) ->id ,
140+ 'itemid ' => $ targetdraftitemid ,
141+ 'contextid ' => $ usercontext ->id ,
125142 'filepath ' => '/ ' ,
126143 'filename ' => $ filemetadata ->filename ,
127144 ], $ file );
0 commit comments