1/**
2 * Integrates move capability into the media manager
3 * Based on the implementation in diagrams plugin
4 */
5class MoveMediaManager {
6
7    constructor() {
8        // user is not allowed to move anything
9        if (!JSINFO.move_allowrename) return;
10
11        const filePanel = document.querySelector('#mediamanager__page .panel.file');
12        if (filePanel) {
13            const observer = new MutationObserver(this.#addMoveButton.bind(this));
14            observer.observe(filePanel, {childList: true, subtree: true});
15        }
16    }
17
18    /**
19     * Observer callback to add the move button in the detail panel of the media manager
20     *
21     * @param mutationsList
22     * @param observer
23     */
24    async #addMoveButton(mutationsList, observer) {
25        for (let mutation of mutationsList) {
26            // div.file has been filled with new content?
27            if (mutation.type !== 'childList') continue;
28
29            // check that the file panel contains a link to a file
30            if (mutation.target.classList.contains('file') === false) continue;
31            const link = mutation.target.querySelector('a.select.mediafile');
32            if (!link) continue;
33
34            const actionList = mutation.target.querySelector('ul.actions');
35            if (actionList.querySelector('button.move-btn')) continue; // already added
36
37            const deleteButton = actionList.querySelector('form#mediamanager__btn_delete');
38            if (deleteButton === null) continue; // no delete permissions
39
40            const src = link.textContent;
41
42            const moveButton = document.createElement('button');
43            moveButton.classList.add('move-btn');
44            moveButton.innerText = LANG.plugins.move.moveButton;
45
46            moveButton.addEventListener('click',  this.#showDialog.bind(this, src));
47            actionList.appendChild(moveButton);
48        }
49    }
50
51    /**
52     * Show the move dialog
53     *
54     * Uses JQuery UI
55     *
56     * @param {string} src
57     * @param {Event} event
58     * @returns {Promise<void>}
59     */
60    async #showDialog(src, event) {
61        event.preventDefault();
62        event.stopPropagation();
63
64        const $form = jQuery(this.#buildForm(src));
65        $form.dialog({
66            title: LANG.plugins.move.moveButton,
67            width: 600,
68            appendTo: '.dokuwiki',
69            modal: true,
70            close: function () {
71                // do not reuse the dialog
72                // https://stackoverflow.com/a/2864783
73                jQuery(this).dialog('destroy').remove();
74            }
75        });
76    }
77
78    /**
79     * Create the form for the old and new file names
80     *
81     * @param {string} src
82     * @returns {HTMLDivElement}
83     */
84    #buildForm(src) {
85        const wrapper = document.createElement('div');
86        const form = document.createElement('form');
87        wrapper.appendChild(form);
88
89        const intro = document.createElement('p');
90        intro.innerText = LANG.plugins.move.dialogIntro;
91        form.appendChild(intro);
92
93        const errorContainer = document.createElement('div');
94        errorContainer.className = 'move-error';
95        form.appendChild(errorContainer);
96
97        const original = document.createElement('input');
98        original.type = 'hidden';
99        original.name = 'move-old-filename';
100        original.value = src;
101        form.appendChild(original);
102
103        const sectok = document.querySelector('form#mediamanager__btn_delete input[name=sectok]').cloneNode();
104        form.appendChild(sectok);
105
106        // strip file extension and put it in a readonly field so it may not be modified
107        const fileExt = document.createElement('input');
108        fileExt.type = 'text';
109        fileExt.readOnly = true;
110        fileExt.size = 5;
111        fileExt.name = 'move-file-ext';
112        fileExt.value = src.split('.').pop();
113
114        const destination = document.createElement('input');
115        destination.type = 'text';
116        destination.className = 'edit';
117        destination.name = 'move-new-filename';
118        destination.value = src.substring(0, src.length - (fileExt.value.length + 1));
119        destination.size = 50;
120        form.appendChild(destination);
121        form.appendChild(fileExt);
122
123        const button = document.createElement('button');
124        button.innerText = LANG.plugins.move.moveButton;
125        form.appendChild(button);
126
127        form.addEventListener('submit', this.#requestMove.bind(this, form));
128
129        return wrapper;
130    }
131
132    /**
133     * Send move request to backend
134     *
135     * @param {HTMLFormElement} form
136     * @param {Event} event
137     * @returns {Promise<void>}
138     */
139    async #requestMove(form, event) {
140
141        event.preventDefault();
142        event.stopPropagation();
143
144        const src = form.querySelector('input[name="move-old-filename"]').value;
145        const dst = form.querySelector('input[name="move-new-filename"]').value;
146        const ext = form.querySelector('input[name="move-file-ext"]').value;
147        const sectok = form.querySelector('input[name="sectok"]').value;
148        const err = form.querySelector('div.move-error');
149
150        jQuery.post(
151            DOKU_BASE + 'lib/exe/ajax.php',
152            {
153                call: 'plugin_move_rename_mediamanager',
154                src: src,
155                dst: dst + '.'  + ext,
156                sectok: sectok
157            },
158            // redirect or display error
159            function (result) {
160                if (result.success) {
161                    window.location.href = result.redirect_url;
162                } else {
163                    err.classList.add('error');
164                    err.innerText = result.error;
165                }
166            }
167        );
168    }
169}
170
171// initialize
172document.addEventListener('DOMContentLoaded', () => {
173    new MoveMediaManager();
174});
175