1 2// multiorphan Admin Plugin Script 3(function($){ 4 5 var canBeStopped = false, $orphanForm = null, $currentPagesAndMedia, $currentResults; 6 7 var ORPHANACTIONS = { 8 9 view : function(type) { 10 return { 11 label: 'View', 12 actionId: 'view', 13 click: function() { 14 var $link = $(this); 15 if (type === 'Page' || type === 'URL') { 16 return true; 17 } 18 request({'do':'view'+type, 'link':decodeURIComponent($link.attr('elementid'))}, function(response){ 19 20 if ( response.dialogContent ) { 21 $('<div/>').attr('id', 'multiorphan__preview_dialog').appendTo('body').dialog({ 22 title:'Preview', 23 height: Math.min(700,$(window).height()-50), 24 width: Math.min(700,$(window).width()-50), 25 autoOpen:true, 26 closeOnEscape:true, 27 modal:true, 28 close: function() { 29 $(this).dialog('close').remove(); 30 } 31 }).html(response.dialogContent); 32 } 33 }); 34 return false; 35 } 36 } 37 }, 38 39 delete : function(type) { 40 return { 41 label: 'Delete', 42 click: function() { 43 var $link = $(this); 44 request({'do':'delete'+type , 'link':decodeURIComponent($link.attr('elementid'))}, function(response){ 45 $link.parents('.entry[elementid="'+$link.attr('elementid')+'"]').addClass('deleted disabled'); 46 $link.parent('.actions').remove(); 47 }); 48 return false; 49 } 50 } 51 }, 52 53 clear: function() { 54 return { 55 label: 'Clear', 56 click: function() { 57 var $link = $(this); 58 $link.parents('.entry[elementid="'+$link.attr('elementid')+'"]').remove(); 59 return false; 60 } 61 } 62 } 63 }; 64 65 var init = function() { 66 $orphanForm = $('form#multiorphan').submit(loadpages); 67 $orphanForm.find( ".multiorphan__result_group" ).accordion({ 68 collapsible: true, 69 active: false, 70 heightStyle: "content" 71 }); 72 reset(); 73 }; 74 75 /** 76 * Load all pages and media 77 * Cycle the result. 78 */ 79 var loadpages = function(event) { 80 81 if ( canBeStopped ) { 82 83 canBeStopped = false; 84 $currentPagesAndMedia.stop = true; 85 $orphanForm.find('button[type=submit]').text(getLang('start')); 86 errorLog(getLang('request-aborted')); 87 return false; 88 } 89 90 reset(true); 91 canBeStopped = true; 92 event.stopPropagation(); 93 $orphanForm.find('button[type=submit]').text(getLang('stop')); 94 request({'do':'loadpages'}, function( $result ){ 95 96 // Start cycling pages 97 $currentPagesAndMedia = $result; 98 $currentPagesAndMedia.interval = 1;//Math.floor($currentPagesAndMedia.pages.length / 10); 99 checkpagesandmedia($.makeArray($result.pages)); 100 }); 101 102 return false; 103 }; 104 105 /** 106 * Time Check while running 107 */ 108 var prevTime = 0; 109 var getTimeDifference = function(){ 110 var newTime = Date.now(); 111 var time = (newTime - (prevTime||newTime)) / 1000; 112 prevTime = newTime; 113 return time + "s"; 114 }; 115 116 /** 117 * Walk the current elements Tree 118 */ 119 var checkpagesandmedia = function(elements) { 120 121 var throttleValue = 0; 122 123 // Cycle pages. Media is implicite. 124 var validateElement = function(result) { 125 126 // Check if we still have elements in the elements list (cycle-list) and in the resultList (could be stopped.) 127 if ( elements && elements.length && !($currentPagesAndMedia && $currentPagesAndMedia.stop) ) { 128 var element = elements.pop(); 129 130 status(getLang('checking-page') + " ("+($currentPagesAndMedia.pages.length-elements.length)+"/"+($currentPagesAndMedia.pages.length)+" " + getTimeDifference() + ")" + (throttleValue > 0 ? ' <i>' + getLang('throttled') + '</i>' : '') + ":<br/>" + element); 131 132 window.setTimeout(function() { 133 request({'do':'checkpage','id':element}, function(response) { 134 checkResponseForWantedAndLinked(response, element); 135 136 // Every 10 pages 137 //if ( elements && elements.length && elements.length % $currentPagesAndMedia.interval == 0 ) { 138 findOrphans(); 139 //} 140 }).always(validateElement); 141 }, Math.max(0, throttleValue) * 1000 ); 142 } else { 143 144 // All done. Check for Orphans. 145 findOrphans(true); 146 147 // Now we can leave. 148 status(getLang('checking-done')); 149 reset(); 150 } 151 }; 152 153 validateElement(); 154 throttleValue = parseInt( $orphanForm.find('input[name=throttle]').val() ); 155 }; 156 157 var buildUrl = function (id) { 158 var cleanedID = decodeURIComponent(id); 159 var schemeSepPos = cleanedID.indexOf('://'); 160 if (schemeSepPos > -1) { 161 var scheme = cleanedID.substr(0, schemeSepPos); 162 if (JSINFO.schemes.indexOf(scheme) > -1) { 163 // we have an external url 164 return cleanedID; 165 } 166 } 167 168 return DOKU_BASE + 'doku.php?id=' + id; 169 }; 170 171 var guiElementActions = function(actions, id, url, $insertPoint) { 172 173 // Add actions 174 var $buttonSet = $('<div/>').addClass('actions').appendTo($insertPoint); 175 $.each(actions||[], function(idx, action) { 176 const attrs = { 177 href: url || buildUrl(id), 178 elementid: id 179 }; 180 181 if (action.actionId === 'view') { 182 attrs.target = '_blank'; 183 } 184 var $link = $('<a>').attr(attrs).text(action.label).appendTo($buttonSet).click(action.click); 185 if ( action.process ) { 186 action.process($link); 187 } 188 }) 189 }; 190 191 /** 192 * Add an entry to the accordion of the according type. 193 */ 194 var addGUIEntry = function($insertPoint, name, url, requestPage, requestPageURL, actions) { 195 196 var id = encodeURIComponent(name); 197 var $header = $insertPoint.prev('.header'); 198 $header.attr('count', parseInt($header.attr('count')||0)+1); 199 200 var $appendTo = $insertPoint.find('.entry[elementid="'+id+'"] > ul'); 201 if ( !$appendTo.length ) { 202 var $wrapper = $('<div/>').addClass('entry').attr('elementid', id).appendTo($insertPoint); 203 $('<span/>').text(name).appendTo($wrapper); 204 205 guiElementActions(actions.concat([ORPHANACTIONS.clear()]), id, url, $wrapper); 206 207 $appendTo = $('<ul/>').appendTo($wrapper); 208 } 209 210 if ( requestPage && requestPage.length ) { 211 var $pageId = $('<span>').text(requestPage); 212 var $entry = $('<li>').addClass('requestPage').append($pageId).appendTo($appendTo); 213 guiElementActions(actions, requestPage, requestPageURL, $entry); 214 } 215 }; 216 217 /** 218 * Build up the structure for linked and wanted pages 219 */ 220 var checkResponseForWantedAndLinked = function(response, requestPage) { 221 222 // Fill the $currentResults object with information. 223 var checkResponse = function( name, url, amount, object, $output, actions ) { 224 225 var checkId = name.split( '#', 2 ).shift(); 226 var checkPoint = amount == 0 ? object.wanted : object.linked; 227 if ( !Array.isArray(checkPoint[checkId]) ) { 228 checkPoint[checkId] = []; 229 } 230 231 if ( checkPoint[checkId].indexOf(requestPage) == -1 ) { 232 checkPoint[checkId].push(requestPage); 233 } 234 235 addGUIEntry($output.find('.multiorphan__result.' + (amount == 0 ? 'wanted' : 'linked')), name, url, requestPage, (response||{}).href, actions); 236 }; 237 238 var $pagesOut = $orphanForm.find('.multiorphan__result_group.pages'); 239 var $mediaOut = $orphanForm.find('.multiorphan__result_group.media'); 240 $.each((response||{}).pages||[], function(page, data){ 241 checkResponse(page, data.href, data.amount, $currentResults.pages, $pagesOut, [ORPHANACTIONS.view('Page')]); 242 }); 243 $.each((response||{}).urls||[], function(page, data){ 244 checkResponse(page, data.href, data.amount, $currentResults.pages, $pagesOut, [ORPHANACTIONS.view('URL')]); 245 }); 246 $.each((response||{}).media||[], function(media, data){ 247 checkResponse(media, data.href, data.amount, $currentResults.media, $mediaOut, [ORPHANACTIONS.view('Media')]); 248 }); 249 }; 250 251 /** 252 * walk all linked pages and remove them from the ones that actually exist in the wiki 253 * assign the result to the array. 254 */ 255 var findOrphans = function(processCompleted) { 256 257 // Sort out all not 258 var orphaned = function(linked, original) { 259 260 if ( !original || !original.length ) return []; 261 var orphaned = $.makeArray(original); // make copy 262 263 $.each(linked, function(link) { 264 if ( (idx = orphaned.indexOf(link)) > -1 ) { 265 orphaned.splice(idx, 1); 266 } 267 }); 268 269 return orphaned; 270 }; 271 272 status(getLang('checking-orphans')); 273 $currentResults.pages.orphan = orphaned($currentResults.pages.linked, $currentPagesAndMedia.pages); 274 $currentResults.media.orphan = orphaned($currentResults.media.linked, $currentPagesAndMedia.media); 275 276 var $pagesOut = $orphanForm.find('.multiorphan__result_group.pages .multiorphan__result.orphan'); 277 var $mediaOut = $orphanForm.find('.multiorphan__result_group.media .multiorphan__result.orphan'); 278 279 $orphanForm.find('.multiorphan__result_group .orphan.header').attr('count', null); 280 281 if ( processCompleted == true ) { 282 $orphanForm.find('.multiorphan__result_group .multiorphan__result.orphan').html(''); 283 $.each($currentResults.pages.orphan, function(idx, orphan){ 284 addGUIEntry($pagesOut, orphan, null, null, null, [ORPHANACTIONS.view('Page'), ORPHANACTIONS.delete('Page')]); 285 }); 286 287 $.each($currentResults.media.orphan, function(idx, orphan){ 288 addGUIEntry($mediaOut, orphan, null, null, null, [ORPHANACTIONS.view('Media'), ORPHANACTIONS.delete('Media')]); 289 }); 290 } else { 291 $orphanForm.find('.multiorphan__result_group .multiorphan__result.orphan').append($('<div/>').html(getLang('please-wait-orphan'))); 292 $pagesOut.prev('.header').attr('count', $currentResults.pages.orphan.length); 293 $mediaOut.prev('.header').attr('count', $currentResults.media.orphan.length); 294 } 295 }; 296 297 /** 298 * Send a request to the plugin. 299 */ 300 var request = function(data, success) { 301 data['ns'] = $orphanForm.find('input[name=ns]').val(); 302 data['filter'] = $orphanForm.find('input[name=filter]').val(); 303 data['sectok'] = $orphanForm.find('input[name=sectok]').val(); 304 305 if ( $orphanForm.find('input[name=purge]').is(':checked') ) { 306 data['purge'] = true 307 } 308 309 if ( $orphanForm.find('input[name=checkExternal]').is(':checked') ) { 310 data['checkExternal'] = true 311 } 312 313 if ( $orphanForm.find('input[name=includeWindowsShares]').is(':checked') ) { 314 data['includeWindowsShares'] = true 315 } 316 317 if ( $orphanForm.find('input[name=includeHidden]').is(':checked') ) { 318 data['includeHidden'] = true 319 } 320 321 // data['type'] = $orphanForm.find('select[name=type]').val() || 'both'; 322 data['call'] = 'multiorphan'; 323 324 throbber(true); 325 return $.post(DOKU_BASE + 'lib/exe/ajax.php', data, handleResponse(success)).always(function(){ 326 throbber(false); 327 }); 328 }; 329 330 /** 331 * Handles Reponses with JSON from AJAX requests. 332 * Also takes care of errors. 333 */ 334 var handleResponse = function( next ) { 335 return function( response ) { 336 // Check for errors 337 var $result; 338 try { 339 $result = $.parseJSON(response); 340 } catch( e ) { 341 throbber(false); 342 return errorLog( getLang('error-parsing') + "\n" + response + "\n\n" + e ); 343 } 344 345 delete response; 346 if ( $result && $result.error ) { 347 reset(); 348 return errorLog( $result.error ); 349 } else { 350 return next( $result ); 351 } 352 } 353 }; 354 355 /** 356 * Set text for status 357 */ 358 var status = function(text) { 359 $('#multiorphan__out').html(text).removeClass('error'); 360 }; 361 362 /** 363 * Log errors into container 364 */ 365 var errorLog = function(text) { 366 367 if (!text || !text.length) { 368 return; 369 } 370 371 if (!$('#multiorphan__errorlog').size()) { 372 $('#multiorphan__out').parent().append($('<div id="multiorphan__errorlog"/>')); 373 } 374 375 var msg = text.split("\n"); 376 for ( var int = 0; int < msg.length; int++) { 377 378 var txtMsg = msg[int]; 379 txtMsg = txtMsg.replace(new RegExp("^runtime error:", "i"), ""); 380 381 if (txtMsg.length == 0) { 382 continue; 383 } 384 385 $('#multiorphan__errorlog').append($('<p/>').text(txtMsg.replace(new RegExp("</?.*?>", "ig"), ""))); 386 } 387 }; 388 389 var resetErrorLog = function() { 390 $('#multiorphan__errorlog').remove(); 391 }; 392 393 /** 394 * Display the loading gif 395 */ 396 var throbberCount = 0; 397 var throbber = function(on) { 398 throbberCount = Math.max(0, throbberCount + (on?1:-1)); 399 $('#multiorphan__throbber').css('visibility', throbberCount>0 ? 'visible' : 'hidden'); 400 }; 401 402 var reset = function(fullReset) { 403 canBeStopped = false; 404 405 // Result Object 406 $currentResults = { 407 408 pages: { 409 linked: {}, 410 wanted: {}, 411 orphan: [] 412 }, 413 media: { 414 linked: {}, 415 wanted: {}, 416 orphan: [] 417 } 418 }; 419 420 // All pages and Media from DW 421 $currentPagesAndMedia = {}; 422 423 throbber(false); 424 $orphanForm.find('button[type=submit]').text(getLang('start')); 425 426 if ( fullReset === true ) { 427 resetErrorLog(); 428 $orphanForm.find('.multiorphan__result_group .header').attr('count', null); 429 $orphanForm.find('.multiorphan__result_group .multiorphan__result').html(''); 430 } 431 }; 432 433 var getLang = function(key) { 434 return LANG.plugins.multiorphan ? LANG.plugins.multiorphan[key] : key; 435 }; 436 437 $(document).ready(init); 438})(jQuery); 439