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