parent
							
								
									5db3236db4
								
							
						
					
					
						commit
						f3891267b8
					
				
					 7 changed files with 262 additions and 36 deletions
				
			
		|  | @ -8,10 +8,16 @@ Install the plugin and put this in your `settings.json`: | |||
| 
 | ||||
|     "ep_delete_after_delay": { | ||||
|         "delay": 86400, // one day, in seconds | ||||
|         "loopDelay": 3600, // one hour, in seconds | ||||
|         "deleteAtStart": true, | ||||
|         "text": "The content of this pad has been deleted since it was older than the configured delay." | ||||
|     }, | ||||
| 
 | ||||
| `delay` (mandatory) is in seconds. You can't put `7*86400` for a week, you have to put `604800`. | ||||
| `delay` (mandatory) delay in seconds with no edition of the pad before deletion. You can't put `7*86400` for a week, you have to put `604800`. | ||||
| 
 | ||||
| `loopDelay` delay in seconds between deletion loops. Deletion loop will check all pads to see if they have to be deleted. You can't put `60*60` for a hour, you have to put `3600`. Default is one hour. | ||||
| 
 | ||||
| `deleteAtStart` binary, tells if you want to start a deletion loop at Etherpad startup. Default is true. | ||||
| 
 | ||||
| `text` is the text that will replace the deleted pad's content. Default is what is in the example above. | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										195
									
								
								delete.js
									
										
									
									
									
								
							
							
						
						
									
										195
									
								
								delete.js
									
										
									
									
									
								
							|  | @ -1,7 +1,12 @@ | |||
| var eejs              = require('ep_etherpad-lite/node/eejs/'), | ||||
|     API               = require('ep_etherpad-lite/node/db/API'), | ||||
|     padManager        = require('ep_etherpad-lite/node/db/PadManager'), | ||||
|     padMessageHandler = require("../../src/node/handler/PadMessageHandler"), | ||||
|     settings          = require('../../src/node/utils/Settings'); | ||||
|     padMessageHandler = require('ep_etherpad-lite/node/handler/PadMessageHandler'), | ||||
|     settings          = require('ep_etherpad-lite/node/utils/Settings'), | ||||
|     async             = require('ep_etherpad-lite/node_modules/async'), | ||||
|     fs                = require('fs'); | ||||
| 
 | ||||
| fs.mkdir('deleted_pads', function(err) {}); | ||||
| 
 | ||||
| // Add client code
 | ||||
| exports.eejsBlock_scripts = function (hook_name, args, cb) { | ||||
|  | @ -10,51 +15,79 @@ exports.eejsBlock_scripts = function (hook_name, args, cb) { | |||
| 
 | ||||
| // Get settings
 | ||||
| var areParamsOk = (settings.ep_delete_after_delay) ? true : false, | ||||
|     delay, replaceText; | ||||
|     delay, replaceText, loopDelay, deleteAtStart; | ||||
| if (areParamsOk) { | ||||
|     delay       = settings.ep_delete_after_delay.delay; | ||||
|     replaceText = settings.ep_delete_after_delay.text || "The content of this pad has been deleted since it was older than the configured delay."; | ||||
|     areParamsOk = (typeof delay === 'number' && delay > 0) ? true : false; | ||||
|     delay         = settings.ep_delete_after_delay.delay; | ||||
|     loopDelay     = settings.ep_delete_after_delay.loopDelay || 3600; | ||||
|     deleteAtStart = settings.ep_delete_after_delay.deleteAtStart || true; | ||||
|     replaceText   = settings.ep_delete_after_delay.text || "The content of this pad has been deleted since it was older than the configured delay."; | ||||
|     areParamsOk   = (typeof delay === 'number' && delay > 0) ? true : false; | ||||
|     if (areParamsOk === false) { | ||||
|         console.error('ep_delete_after_delay.delay must be a number an not negative! Check you settings.json.'); | ||||
|     } | ||||
|     areParamsOk = (typeof loopDelay === 'number' && loopDelay > 0) ? true : false; | ||||
|     if (areParamsOk === false) { | ||||
|         console.error('ep_delete_after_delay.loopDelay must be a number an not negative! Check you settings.json.'); | ||||
|     } | ||||
| } else { | ||||
|     console.error('You need to configure ep_delete_after_delay in your settings.json!'); | ||||
| } | ||||
| 
 | ||||
| exports.handleMessage = function(hook_name, context, cb) { | ||||
|     if (areParamsOk === false) return false; | ||||
| // Recurring deletion function
 | ||||
| var waitForIt = function() { | ||||
|     setTimeout(function() { | ||||
|         console.info('New loop'); | ||||
|         delete_old_pads(); | ||||
|         waitForIt(); | ||||
|     }, loopDelay * 1000); | ||||
| }; | ||||
| 
 | ||||
|     var message = context.message, | ||||
|            type = message.type; | ||||
|     if (type === 'CLIENT_READY' || type === 'COLLABROOM') { | ||||
|         var padId = (type === 'CLIENT_READY') ? message.padId : context.client.rooms[1]; | ||||
|         padManager.getPad(padId, function(callback, pad) { | ||||
|             //
 | ||||
| // Delete old pads at startup
 | ||||
| if (deleteAtStart) { | ||||
|     delete_old_pads(); | ||||
| } | ||||
| 
 | ||||
| // start the recurring deletion loop
 | ||||
| waitForIt(); | ||||
| 
 | ||||
| // deletion loop
 | ||||
| function delete_old_pads() { | ||||
|     // Deletion queue (avoids max stack size error), 2 workers
 | ||||
|     var q = async.queue(function (pad, callback) { | ||||
|         API.getHTML(pad.id, function(err, d) { | ||||
|             if (err) { | ||||
|                 return callback(err); | ||||
|             } | ||||
|             var currentTime = (new Date).getTime(); | ||||
|             fs.writeFile('deleted_pads/'+pad.id+'-'+currentTime+'.html', d.html, function(err) { | ||||
|                 pad.remove(callback); | ||||
|             }); | ||||
|         }); | ||||
|     }, 2); | ||||
|     // Emptyness test queue
 | ||||
|     var p = async.queue(function(padId, callback) { | ||||
|         padManager.getPad(padId, function(err, pad) { | ||||
|             // If this is a new pad, there's nothing to do
 | ||||
|             if (pad.getHeadRevisionNumber() !== 0) { | ||||
| 
 | ||||
|             var head = pad.getHeadRevisionNumber(); | ||||
|             if (head !== null  && head !== undefined && head !== 0) { | ||||
|                 pad.getLastEdit(function(callback, timestamp) { | ||||
|                     if (timestamp !== undefined && timestamp !== null) { | ||||
|                         var currentTime = (new Date).getTime(); | ||||
| 
 | ||||
|                         // Are we over delay?
 | ||||
|                         if ((currentTime - timestamp) > (delay * 1000)) { | ||||
| 
 | ||||
|                             console.debug('Pushing %s to q queue', pad.id); | ||||
|                             // Remove pad
 | ||||
|                             padManager.removePad(padId); | ||||
|                             console.info('Pad '+padId+' deleted since expired (delay: '+delay+' seconds, last edition: '+timestamp+').'); | ||||
| 
 | ||||
|                             // Create new pad with an explanation
 | ||||
|                             padManager.getPad(padId, replaceText, function() { | ||||
|                                 if (type === 'COLLABROOM') { | ||||
|                             q.push(pad, function (err) { | ||||
|                                 console.info('Pad '+pad.id+' deleted since expired (delay: '+delay+' seconds, last edition: '+timestamp+').'); | ||||
|                                 // Create new pad with an explanation
 | ||||
|                                 padManager.getPad(padId, replaceText, function() { | ||||
|                                     // Create disconnect message
 | ||||
|                                     var msg = { | ||||
|                                         type: "COLLABROOM", | ||||
|                                         data: { | ||||
|                                             type: "CUSTOM", | ||||
|                                             payload: { | ||||
|                                                 authorId: message.authorId, | ||||
|                                                 authorId: null, | ||||
|                                                 action: "requestRECONNECT", | ||||
|                                                 padId: padId | ||||
|                                             } | ||||
|  | @ -68,10 +101,88 @@ exports.handleMessage = function(hook_name, context, cb) { | |||
|                                             // TODO: Error handling
 | ||||
|                                         }); // Send a message to this session
 | ||||
|                                     }); | ||||
|                                     cb(null); | ||||
|                                 } else { | ||||
|                                     cb(); | ||||
|                                 }); | ||||
|                             }); | ||||
|                         } else { | ||||
|                             console.debug('Nothing to do with '+padId+' (not expired)'); | ||||
|                         } | ||||
|                     } | ||||
|                 }); | ||||
|             } else { | ||||
|                 console.debug('New or empty pad '+padId); | ||||
|             } | ||||
|             callback(); | ||||
|         }); | ||||
|     }, 1); | ||||
|     padManager.listAllPads(function (err, data) { | ||||
|         for (var i = 0; i < data.padIDs.length; i++) { | ||||
|             var padId = data.padIDs[i]; | ||||
|             console.debug('Pushing %s to p queue', padId); | ||||
|             p.push(padId, function (err) { }); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| exports.handleMessage = function(hook_name, context, cb) { | ||||
|     if (areParamsOk === false) return false; | ||||
| 
 | ||||
|     var message = context.message, | ||||
|            type = message.type; | ||||
|     if (type === 'CLIENT_READY' || type === 'COLLABROOM') { | ||||
|         var padId = (type === 'CLIENT_READY') ? message.padId : context.client.rooms[1]; | ||||
|         padManager.getPad(padId, function(callback, pad) { | ||||
| 
 | ||||
|             // If this is a new pad, there's nothing to do
 | ||||
|             if (pad.getHeadRevisionNumber() !== 0) { | ||||
| 
 | ||||
|                 pad.getLastEdit(function(callback, timestamp) { | ||||
|                     if (timestamp !== undefined && timestamp !== null) { | ||||
|                         var currentTime = (new Date).getTime(); | ||||
| 
 | ||||
|                         // Are we over delay?
 | ||||
|                         if ((currentTime - timestamp) > (delay * 1000)) { | ||||
| 
 | ||||
|                             API.getHTML(padId, function(err, d) { | ||||
|                                 if (err) { | ||||
|                                     return cb(err); | ||||
|                                 } | ||||
|                                 fs.writeFile('deleted_pads/'+padId+'-'+currentTime+'.html', d.html, function(err) { | ||||
|                                     if (err) { | ||||
|                                         return cb(err); | ||||
|                                     } | ||||
|                                     // Remove pad
 | ||||
|                                     padManager.removePad(padId); | ||||
|                                     console.info('Pad '+padId+' deleted since expired (delay: '+delay+' seconds, last edition: '+timestamp+').'); | ||||
| 
 | ||||
|                                     // Create new pad with an explanation
 | ||||
|                                     padManager.getPad(padId, replaceText, function() { | ||||
|                                         // Create disconnect message
 | ||||
|                                         var msg = { | ||||
|                                             type: "COLLABROOM", | ||||
|                                             data: { | ||||
|                                                 type: "CUSTOM", | ||||
|                                                 payload: { | ||||
|                                                     authorId: message.authorId, | ||||
|                                                     action: "requestRECONNECT", | ||||
|                                                     padId: padId | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         }; | ||||
|                                         // Send disconnect message to all clients
 | ||||
|                                         var sessions = padMessageHandler.sessioninfos; | ||||
|                                         Object.keys(sessions).forEach(function(key){ | ||||
|                                             var session = sessions[key]; | ||||
|                                             padMessageHandler.handleCustomObjectMessage(msg, false, function(){ | ||||
|                                                 // TODO: Error handling
 | ||||
|                                             }); // Send a message to this session
 | ||||
|                                         }); | ||||
|                                         if (type === 'COLLABROOM') { | ||||
|                                             cb(null); | ||||
|                                         } else { | ||||
|                                             cb(); | ||||
|                                         } | ||||
|                                     }); | ||||
|                                 }); | ||||
|                             }); | ||||
|                         } else { | ||||
|                             console.info('Nothing to do with '+padId+' (not expired)'); | ||||
|  | @ -89,6 +200,30 @@ exports.handleMessage = function(hook_name, context, cb) { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| // Send
 | ||||
| function sendToTarget(message, msg){ | ||||
| exports.registerRoute  = function (hook_name, args, cb) { | ||||
|     args.app.get('/ttl/:pad', function(req, res, next) { | ||||
|         var padId = req.params.pad; | ||||
| 
 | ||||
|         res.header("Access-Control-Allow-Origin", "*"); | ||||
|         res.setHeader('Content-Type', 'application/json'); | ||||
| 
 | ||||
|         padManager.getPad(padId, function(callback, pad) { | ||||
| 
 | ||||
|             // If this is a new pad, there's nothing to do
 | ||||
|             if (pad.getHeadRevisionNumber() !== 0) { | ||||
| 
 | ||||
|                 pad.getLastEdit(function(callback, timestamp) { | ||||
|                     if (timestamp !== undefined && timestamp !== null) { | ||||
|                         var currentTime = (new Date).getTime(); | ||||
| 
 | ||||
|                         var ttl = Math.floor((delay * 1000 - (currentTime - timestamp))/1000); | ||||
|                         res.send('{"ttl": '+ttl+'}'); | ||||
|                     } | ||||
|                 }); | ||||
|             } else { | ||||
|                 res.send('{"ttl": null, "msg": "New or empty pad"}'); | ||||
|             } | ||||
|             cb && cb(); | ||||
|         }); | ||||
|     }); | ||||
| } | ||||
|  |  | |||
							
								
								
									
										6
									
								
								ep.json
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								ep.json
									
										
									
									
									
								
							|  | @ -4,10 +4,12 @@ | |||
|             "name": "ep_delete_after_delay", | ||||
|             "hooks": { | ||||
|                 "handleMessage" : "ep_delete_after_delay/delete", | ||||
|                 "eejsBlock_scripts": "ep_delete_after_delay/delete" | ||||
|                 "eejsBlock_scripts": "ep_delete_after_delay/delete", | ||||
|                 "expressCreateServer" : "ep_delete_after_delay/delete:registerRoute" | ||||
|             }, | ||||
|             "client_hooks": { | ||||
|                 "handleClientMessage_CUSTOM":"ep_delete_after_delay/static/js/reconnect" | ||||
|                 "handleClientMessage_CUSTOM":"ep_delete_after_delay/static/js/reconnect", | ||||
|                 "documentReady": "ep_delete_after_delay/static/js/reconnect" | ||||
|             } | ||||
|         } | ||||
|     ] | ||||
|  |  | |||
							
								
								
									
										9
									
								
								locales/en.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								locales/en.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| { | ||||
|     "ep_delete_after_delay.warning": "❎ WARNING!", | ||||
|     "ep_delete_after_delay.reload": "The pad has expired. The page will be reloaded in 5 seconds.", | ||||
|     "ep_delete_after_delay.close": "❎ Delay before deletion", | ||||
|     "ep_delete_after_delay.days": "Without edition, your pad will be deleted in approximatively XXXX days.", | ||||
|     "ep_delete_after_delay.hours": "Without edition, your pad will be deleted in approximatively XXXX hours.", | ||||
|     "ep_delete_after_delay.minutes": "Without edition, your pad will be deleted in approximatively XXXX minutes.", | ||||
|     "ep_delete_after_delay.suggest": "I suggest you to either edit your pad or export it to save your work." | ||||
| } | ||||
							
								
								
									
										9
									
								
								locales/fr.json
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								locales/fr.json
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| { | ||||
|     "ep_delete_after_delay.warning": "❎ ATTENTION !", | ||||
|     "ep_delete_after_delay.reload": "Le pad a expiré. La page sera rechargée dans 5 secondes.", | ||||
|     "ep_delete_after_delay.close": "❎ Délai avant suppression", | ||||
|     "ep_delete_after_delay.days": "Sans édition, votre pad sera supprimé dans approximativement XXXX jours.", | ||||
|     "ep_delete_after_delay.hours": "Sans édition, votre pad sera supprimé dans approximativement XXXX heures.", | ||||
|     "ep_delete_after_delay.minutes": "Sans édition, votre pad sera supprimé dans approximativement XXXX minutes.", | ||||
|     "ep_delete_after_delay.suggest": "Je vous suggère d'éditer votre pad ou de l'exporter pour sauvegarder votre travail." | ||||
| } | ||||
|  | @ -1,7 +1,7 @@ | |||
| 
 | ||||
| { | ||||
|     "name": "ep_delete_after_delay", | ||||
|     "version": "0.0.3", | ||||
|     "version": "0.0.4", | ||||
|     "description": "Automatically deletes pads after a configured delay", | ||||
|     "author": { | ||||
|         "name": "Luc Didry", | ||||
|  |  | |||
|  | @ -1,8 +1,73 @@ | |||
| if(typeof exports == 'undefined'){ | ||||
|     var exports = this['mymodule'] = {}; | ||||
| } | ||||
| 
 | ||||
| var already_notified = false; | ||||
| var delay; | ||||
| 
 | ||||
| function get_ttl(callback) { | ||||
|     $.ajax({ | ||||
|         url: '../ttl/'+window.location.pathname.replace(/.*\/p\//, ''), | ||||
|         method: 'GET', | ||||
|         dataType: 'json', | ||||
|         success: function(data, textStatus, jqXHR) { | ||||
|             $('.ttl').remove(); | ||||
|             if (data.ttl === null) { | ||||
|                 delay = 1000; | ||||
|             } | ||||
|             if (data.ttl !== null && data.ttl > 0) { | ||||
|                 var text, ttl; | ||||
|                 if (data.ttl > 3600 * 24) { | ||||
|                     text  = window._('ep_delete_after_delay.days'); | ||||
|                     ttl   = data.ttl/(3600 * 24); | ||||
|                     delay = 3500 * 1000; | ||||
|                 } else if (data.ttl > 3600) { | ||||
|                     text  = window._('ep_delete_after_delay.hours'); | ||||
|                     ttl   = data.ttl/3600; | ||||
|                     delay = 1800 * 1000; // 30 minutes
 | ||||
|                 } else { | ||||
|                     text  = window._('ep_delete_after_delay.minutes'); | ||||
|                     ttl   = data.ttl/60; | ||||
|                     delay = 600 * 1000 // 10 minutes
 | ||||
|                 } | ||||
|                 ttl  = Math.floor(ttl * 10)/10; | ||||
|                 text = text.replace(/XXXX/, ttl); | ||||
|                 $.gritter.add({ | ||||
|                     class_name: 'ttl', | ||||
|                     title: window._('ep_delete_after_delay.close'), | ||||
|                     text: text+'<br>'+window._('ep_delete_after_delay.suggest'), | ||||
|                     sticky: true, | ||||
|                 }); | ||||
|             } | ||||
|         }, | ||||
|         complete: function() { | ||||
|             callback(delay); | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| function timeout_ttl(delay) { | ||||
|     setTimeout(function() { | ||||
|         get_ttl(timeout_ttl); | ||||
|     }, delay); | ||||
| } | ||||
| 
 | ||||
| exports.handleClientMessage_CUSTOM = function(hook, context, wut){ | ||||
|     if(context.payload.action === 'requestRECONNECT'){ | ||||
|         window.location.reload(); | ||||
|     if(already_notified === false && context.payload && context.payload.action === 'requestRECONNECT'){ | ||||
|         already_notified = true; | ||||
|         $.gritter.add({ | ||||
|             title: window._('ep_delete_after_delay.warning'), | ||||
|             text: window._('ep_delete_after_delay.reload'), | ||||
|             sticky: true, | ||||
|         }); | ||||
|         setTimeout(function() { | ||||
|             window.location.reload(); | ||||
|         }, 6000); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| exports.documentReady = function() { | ||||
|     setTimeout(function() { | ||||
|         get_ttl(timeout_ttl); | ||||
|     }, 1000); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Luc Didry
						Luc Didry