xref: /plugin/bookmark2wiki/action.php (revision fd5d3c8a122aefbeb8d5725eeb71f2886bbd14cf)
1<?php
2/**
3 * DokuWiki Plugin bookmark2wiki (Action Component)
4 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
5 * @author dodotori <dodotori@localhost>
6 * forked from post2wiki.php by riny [at] bk [dot] ru
7 * To bookmark webpage using bookmarklet
8 * The app will add the url, title and hightlighed text you want to the end of the content of the targeted namespace. It does not directly read/write the dokuwiki page folder.
9 * Version 1.0
10 * @license GPL 2[](http://www.gnu.org/licenses/gpl.html)
11 * @author dodotori https://github.com/edwardcodelab
12 */
13// TYPICAL USAGE :
14// Create bookmarklet as shown in the bookmarklet part below:
15// Change the window.open statement to reflect the location of the bookmark2wiki.php script.
16// Drag bookmarklet to your toolbar.
17// BOOKMARKLET :
18// javascript:Q=document.selection?document.selection.createRange().text:document.getSelection(); void(window.open('https://myserver/doku.php?do=bookmark2wiki&te='+escape(Q)+'&ur='+ escape(location.href)+'&ti='+escape(document.title),'dokuwikiadd','scrollbars=yes,resizable=yes,toolbars=yes,width=400,height=200,left=200,top=200,status=yes'));
19
20use dokuwiki\Extension\ActionPlugin;
21use dokuwiki\Extension\EventHandler;
22use dokuwiki\Extension\Event;
23
24class action_plugin_bookmark2wiki extends ActionPlugin
25{
26    /** @inheritDoc */
27    public function register(EventHandler $controller)
28    {
29        $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'allowMyAction');
30        $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'performMyAction');
31    }
32
33    /**
34     * Event handler for ACTION_ACT_PREPROCESS
35     *
36     * @param Event $event event object by reference
37     * @param mixed $param optional parameter passed when event was registered
38     * @return void
39     */
40    public function allowMyAction(Event $event, $param) {
41        if($event->data != 'bookmark2wiki') return;
42        $event->preventDefault();
43    }
44
45    /**
46     * Event handler for TPL_ACT_UNKNOWN
47     *
48     * @param Event $event event object by reference
49     * @param mixed $param optional parameter passed when event was registered
50     * @return void
51     */
52    public function performMyAction(Event $event, $param) {
53       if($event->data != 'bookmark2wiki') return;
54       $event->preventDefault();
55
56       // Get params (support both GET from bookmarklet and POST from form)
57       $te = $_REQUEST['te'] ?? '';
58       $ur = $_REQUEST['ur'] ?? '';
59       $ti = $_REQUEST['ti'] ?? '';
60
61       // Decode unicode (from original)
62       $string = preg_replace('/%u([0-9A-F]+)/', '&#x$1;', $ti);
63       $ti = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
64       $string = preg_replace('/%u([0-9A-F]+)/', '&#x$1;', $te);
65       $te = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
66
67       if (!$ur || !$ti) {
68           echo 'Error: Missing URL or title.';
69           return;
70       }
71
72       // Basic URL validation
73       if (!filter_var($ur, FILTER_VALIDATE_URL)) {
74           echo 'Error: Invalid URL.';
75           return;
76       }
77
78       $timestamp = date("Y:m:d:H:i:s");
79       $new_entry = "  * [[" . $ur . "|" . $ti . "]] \\\\ " . $te . " -- " . $timestamp;
80
81       if (isset($_POST['save'])) {
82           // Handle form submit: directly save using DokuWiki functions
83           $namespace = cleanID($_POST['page']);
84           if (!$namespace) {
85               echo 'Error: No page selected.';
86               return;
87           }
88
89           // Check edit permission
90           if (auth_quickaclcheck($namespace) < AUTH_EDIT) {
91               echo 'Error: No permission to edit the selected page.';
92               return;
93           }
94
95           // Get raw position config and map to internal value
96           $raw_position = $this->getConf('position');
97           $position_map = [
98               'After Heading (prepend)' => 'top',
99               'End of Page (append)' => 'bottom'
100           ];
101           $position = isset($position_map[$raw_position]) ? $position_map[$raw_position] : $raw_position;
102           // Fallback to 'bottom' if config is invalid
103           if (!in_array($position, ['top', 'bottom'])) {
104               $position = 'bottom';
105           }
106
107           // Read current page content
108           $text = rawWiki($namespace);
109           $text = str_replace("\r", "", $text);  // Normalize line endings to \n
110
111           if (trim($text) === '') {
112               $text = "====== New Bookmarks ====== \n";
113           }
114
115           $lines = explode("\n", $text);
116
117           if ($position == 'top') {
118               // Find the position after the first heading (match after trim)
119               $insert_pos = 0;
120               $found_heading = false;
121               for ($i = 0; $i < count($lines); $i++) {
122                   $trimmed = trim($lines[$i]);
123                   if (preg_match('/^=+.*=+$/', $trimmed)) {
124                       $insert_pos = $i + 1;
125                       $found_heading = true;
126                       break;
127                   }
128               }
129
130               // Skip empty lines after the heading
131               while ($insert_pos < count($lines) && trim($lines[$insert_pos]) === '') {
132                   $insert_pos++;
133               }
134
135               // Insert the new entry
136               array_splice($lines, $insert_pos, 0, $new_entry);
137           } else {
138               // Append to end, but add a newline if not empty
139               if (!empty(trim($lines[count($lines) - 1]))) {
140                   $lines[] = '';
141               }
142               $lines[] = $new_entry;
143           }
144
145           $newtext = implode("\n", $lines);
146
147           // Save the updated content
148           saveWikiText($namespace, $newtext, 'Added bookmark: ' . $ti, false);  // false = not minor edit
149
150           echo 'Bookmark saved to ' . hsc($namespace) . '! <script>setTimeout(function(){window.close();}, 3000);</script>';
151       } else {
152           // Display form for page selection
153           $configured_pages = explode("\n", trim($this->getConf('pages')));
154           if (empty($configured_pages)) {
155               echo 'Error: No pages configured in plugin settings.';
156               return;
157           }
158
159           echo '<form method="post" action="">';
160           echo '<label for="page">Save to page:</label><br>';
161           echo '<select name="page" id="page">';
162           foreach ($configured_pages as $p) {
163               $p = trim($p);
164               if ($p) {
165                   echo '<option value="' . hsc($p) . '">' . hsc($p) . '</option>';
166               }
167           }
168           echo '</select><br>';
169           echo '<input type="hidden" name="te" value="' . hsc($te) . '">';
170           echo '<input type="hidden" name="ur" value="' . hsc($ur) . '">';
171           echo '<input type="hidden" name="ti" value="' . hsc($ti) . '">';
172           echo '<input type="submit" name="save" value="Save">';
173           echo '</form>';
174       }
175    }
176}