Moved plugin web files to be in the plugin folder to reduce staticfiles blueprint coupling
This commit is contained in:
		
							parent
							
								
									14f2d03ebf
								
							
						
					
					
						commit
						37e5dbab4b
					
				
					 23 changed files with 37 additions and 40 deletions
				
			
		|  | @ -8,6 +8,7 @@ import os | |||
| import ujson as json | ||||
| 
 | ||||
| from flask import Response, Blueprint | ||||
| from flask import send_from_directory | ||||
| from deadsimplekv import DeadSimpleKV | ||||
| 
 | ||||
| from utils import identifyhome | ||||
|  | @ -28,6 +29,9 @@ from utils import identifyhome | |||
| 
 | ||||
| flask_blueprint = Blueprint('circles', __name__) | ||||
| 
 | ||||
| root = os.path.dirname(os.path.realpath(__file__)) | ||||
| 
 | ||||
| 
 | ||||
| with open( | ||||
|     os.path.dirname( | ||||
|         os.path.realpath(__file__)) + '/info.json', 'r') as info_file: | ||||
|  | @ -41,6 +45,15 @@ read_only_cache = DeadSimpleKV( | |||
|     flush_on_exit=False, | ||||
|     refresh_seconds=30) | ||||
| 
 | ||||
| @flask_blueprint.route('/board/<path:path>', endpoint='circlesstatic') | ||||
| def load_mail(path): | ||||
|     return send_from_directory(root + '/web/', path) | ||||
| 
 | ||||
| 
 | ||||
| @flask_blueprint.route('/board/', endpoint='circlesindex') | ||||
| def load_mail_index(): | ||||
|     return send_from_directory(root + '/web/', 'index.html') | ||||
| 
 | ||||
| 
 | ||||
| @flask_blueprint.route('/circles/getpostsbyboard/<board>') | ||||
| def get_post_by_board(board): | ||||
|  |  | |||
|  | @ -36,7 +36,7 @@ import flowapi  # noqa | |||
| """ | ||||
| 
 | ||||
| flask_blueprint = flowapi.flask_blueprint | ||||
| security_whitelist = ['staticfiles.boardContent', 'staticfiles.board'] | ||||
| security_whitelist = ['circles.circlesstatic', 'circles.circlesindex'] | ||||
| 
 | ||||
| plugin_name = 'circles' | ||||
| PLUGIN_VERSION = '0.1.0' | ||||
|  |  | |||
							
								
								
									
										44
									
								
								static-data/default-plugins/circles/web/autorefresh.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								static-data/default-plugins/circles/web/autorefresh.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Auto refresh board posts | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| var checkbox = document.getElementById('refreshCheckbox') | ||||
| function autoRefresh(){ | ||||
|     if (! checkbox.checked || document.hidden){return} | ||||
|     getBlocks() | ||||
| } | ||||
| 
 | ||||
| function setupInterval(){ | ||||
|     if (checkbox.checked){ | ||||
|         refreshInterval = setInterval(autoRefresh, 3000) | ||||
|         autoRefresh() | ||||
|         return | ||||
|     } | ||||
|     clearInterval(refreshInterval) | ||||
| } | ||||
| 
 | ||||
| var refreshInterval = setInterval(autoRefresh, 3000) | ||||
| setupInterval() | ||||
| 
 | ||||
| checkbox.onchange = function(){setupInterval} | ||||
| 
 | ||||
| 
 | ||||
| document.addEventListener("visibilitychange", function() { | ||||
|     if (document.visibilityState === 'visible') { | ||||
|       autoRefresh() | ||||
|     } | ||||
|   }) | ||||
							
								
								
									
										260
									
								
								static-data/default-plugins/circles/web/board.js
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										260
									
								
								static-data/default-plugins/circles/web/board.js
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,260 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     This file handles the boards/circles interface | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| requested = [] | ||||
| newPostForm = document.getElementById('addMsg') | ||||
| firstLoad = true | ||||
| lastLoadedBoard = 'global' | ||||
| loadingMessage = document.getElementById('loadingBoard') | ||||
| loadedAny = false | ||||
| loadingTimeout = 8000 | ||||
| 
 | ||||
| let toggleLoadingMessage = function(){ | ||||
|     switch (loadingMessage.style.display){ | ||||
|         case "inline-block": | ||||
|             loadingMessage.style.display = "none" | ||||
|             break; | ||||
|         default: | ||||
|             loadingMessage.style.display = "initial" | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fetch('/circles/version', { | ||||
|     method: 'GET', | ||||
|     headers: { | ||||
|       "token": webpass | ||||
| }}) | ||||
| .then((ver) => ver.text()) | ||||
| .then(function(ver) { | ||||
|     document.getElementById('circlesVersion').innerText = ver | ||||
| }) | ||||
| 
 | ||||
| function appendMessages(msg, blockHash, beforeHash, channel) { | ||||
|     if (channel !== document.getElementById('feedIDInput').value) return // ignore if channel name isn't matching
 | ||||
|     if (msg.length == 0) return // ignore empty messages
 | ||||
| 
 | ||||
|     var humanDate = new Date(0) | ||||
|     var msgDate = msg['meta']['time'] | ||||
|     var feed = document.getElementById("feed") | ||||
|     var beforeEl = null | ||||
| 
 | ||||
|     if (msgDate === undefined){ | ||||
|         msgDate = 'unknown' | ||||
|     } else { | ||||
|         humanDate.setUTCSeconds(msgDate) | ||||
|         msgDate = humanDate.toLocaleString("en-US", {timeZone: "Etc/GMT"}) | ||||
|     } | ||||
| 
 | ||||
|     var el = document.createElement('div') | ||||
|     el.innerText = msg['content'] | ||||
| 
 | ||||
|     if (beforeHash !== null) { | ||||
|         for (i = 0; i < feed.children.length; i++) { | ||||
|             if (feed.children[i].getAttribute('data-bl') === beforeHash) { | ||||
|                 beforeEl = feed.children[i] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Template Test */ | ||||
|     // Test to see if the browser supports the HTML template element by checking
 | ||||
|     // for the presence of the template element's content attribute.
 | ||||
|     if ('content' in document.createElement('template')) { | ||||
| 
 | ||||
|         // Instantiate the table with the existing HTML tbody
 | ||||
|         // and the row with the template
 | ||||
|         var template = document.getElementById('cMsgTemplate') | ||||
| 
 | ||||
|         // Clone the new row and insert it into the table
 | ||||
|         var clone = document.importNode(template.content, true) | ||||
|         var div = clone.querySelectorAll("div") | ||||
|         var identicon = clone.querySelectorAll("img") | ||||
| 
 | ||||
|         div[0].classList.add('entry') | ||||
|         div[0].setAttribute('timestamp', msg['meta']['time']) | ||||
| 
 | ||||
|         div[0].setAttribute('data-bl', blockHash) | ||||
|         div[2].textContent = msg['content'] | ||||
|         if (typeof msg['meta']['signer'] != 'undefined' && msg['meta']['signer'].length > 0){ | ||||
|             div[3].textContent = msg['meta']['signer'].substr(0, 5) | ||||
|             setHumanReadableIDOnPost(div[3], msg['meta']['signer']) | ||||
|             div[3].title = msg['meta']['signer'] | ||||
|             userIcon(msg['meta']['signer']).then(function(data){ | ||||
|                 identicon[0].src = "data:image/svg+xml;base64," + data | ||||
|             }) | ||||
|         } | ||||
|         else{ | ||||
|             identicon[0].remove() | ||||
|         } | ||||
|         div[4].textContent = msgDate | ||||
| 
 | ||||
|         loadingMessage.style.display = "none" | ||||
|         loadedAny = true | ||||
|         if (firstLoad){ | ||||
|             //feed.appendChild(clone)
 | ||||
|             feed.prepend(clone) | ||||
|             firstLoad = false | ||||
|         } | ||||
|         else{ | ||||
|             if (beforeEl === null){ | ||||
|                 feed.prepend(clone) | ||||
|             } | ||||
|             else{ | ||||
|                 beforeEl.insertAdjacentElement("beforebegin", clone.children[0]) | ||||
|             } | ||||
| 
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function getBlocks(){ | ||||
|     var feed = document.getElementById("feed") | ||||
|     var ch = document.getElementById('feedIDInput').value | ||||
|     if (lastLoadedBoard !== ch){ | ||||
|         requested = [] | ||||
| 
 | ||||
|         toggleLoadingMessage() | ||||
|         loadedAny = false | ||||
| 
 | ||||
|         while (feed.firstChild) feed.removeChild(feed.firstChild); // remove all messages from feed
 | ||||
| 
 | ||||
|         setTimeout(function(){ | ||||
|             if (! loadedAny && ch == document.getElementById('feedIDInput').value){ | ||||
|                 PNotify.notice("There are no posts for " + ch + ". You can be the first!") | ||||
|             } | ||||
|         }, loadingTimeout) | ||||
|     } | ||||
| 
 | ||||
|     lastLoadedBoard = ch | ||||
|     if (document.getElementById('none') !== null){ | ||||
|         document.getElementById('none').remove(); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     fetch('/circles/getpostsbyboard/' + ch, { | ||||
|         method: 'GET', | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|     }}) | ||||
|     .then((resp) => resp.text()) | ||||
|     .then(function(feedText) { | ||||
|         var blockList = feedText.split(',') | ||||
| 
 | ||||
|         for (i = 0; i < blockList.length; i++){ | ||||
|             blockList[i] = "0".repeat(64 - blockList[i].length) + blockList[i] // pad hash with zeroes
 | ||||
| 
 | ||||
|             if (! requested.includes(blockList[i])){ | ||||
|                 if (blockList[i].length == 0) continue | ||||
|                 else requested.push(blockList[i]) | ||||
|                 loadMessage(blockList[i], blockList, i, ch); | ||||
|             } | ||||
|         } | ||||
|         sortEntries() | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| function loadMessage(blockHash, blockList, count, channel){ | ||||
|     if (blockHash == '0000000000000000000000000000000000000000000000000000000000000000'){ | ||||
|         return | ||||
|     } | ||||
|     fetch('/getblockdata/' + blockHash, { | ||||
|         method: 'GET', | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|     }}).then(function(response) { | ||||
|         if (!response.ok) { | ||||
|             let on404 = function() { | ||||
|             if (response.status == 404){ | ||||
|                 fetch('/circles/removefromcache/' + channel + '/' + blockHash, { | ||||
|                     method: 'POST', | ||||
|                     headers: { | ||||
|                         "content-type": "application/json", | ||||
|                         "token": webpass | ||||
|                     } | ||||
|                 }) | ||||
|             } | ||||
|             else{ | ||||
|                 console.log(error) | ||||
|             } | ||||
|             }() | ||||
|             return | ||||
|         } | ||||
|         response.json().then(function(data){ | ||||
|             let before = blockList[count - 1] | ||||
|             let delay = 2000 | ||||
|             if (typeof before == "undefined"){ | ||||
|                 before = null | ||||
|             } else { | ||||
|                 let existing = document.getElementsByClassName('cMsgBox') | ||||
|                 for (x = 0; x < existing.length; x++){ | ||||
|                     if (existing[x].getAttribute('data-bl') === before){ | ||||
|                         delay = 0 | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             setTimeout(function(){appendMessages(data, blockHash, before, channel)}, delay) | ||||
|         }) | ||||
|         return response; | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| document.getElementById('refreshFeed').onclick = function() { | ||||
|     getBlocks() | ||||
| } | ||||
| 
 | ||||
| newPostForm.onsubmit = function(){ | ||||
|     var message = document.getElementById('newMsgText').value | ||||
|     var channel = document.getElementById('feedIDInput').value | ||||
|     var meta = {'ch': channel} | ||||
|     let doSign = document.getElementById('postAnon').checked | ||||
|     var postData = {'message': message, 'sign': doSign, 'type': 'brd', 'encrypt': false, 'meta': JSON.stringify(meta)} | ||||
|     postData = JSON.stringify(postData) | ||||
|     newPostForm.style.display = 'none' | ||||
|     fetch('/insertblock', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         } | ||||
|     }) | ||||
|     .then((resp) => resp.text()) | ||||
|     .then(function(data) { | ||||
|         newPostForm.style.display = 'block' | ||||
|         if (data == 'failure due to duplicate insert'){ | ||||
|             PNotify.error({ | ||||
|                 text: "This message is already queued" | ||||
|             }) | ||||
|             return | ||||
|         } | ||||
|         PNotify.success({ | ||||
|             text: "Message queued for posting" | ||||
|         }) | ||||
|         setTimeout(function(){getBlocks()}, 500) | ||||
|     }) | ||||
|     return false | ||||
| } | ||||
| 
 | ||||
| resetCirclePickers = function(){ | ||||
|     document.getElementById('recommendedBoards').value = "" | ||||
|     document.getElementById('popularBoards').value = "" | ||||
| } | ||||
| 
 | ||||
| document.getElementById('feedIDInput').onchange = resetCirclePickers | ||||
| 
 | ||||
|  | @ -0,0 +1,26 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Handle default board picker | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| recommendedIDs = document.getElementById('recommendedBoards') | ||||
| 
 | ||||
| recommendedIDs.onchange = function(){ | ||||
|     document.getElementById('feedIDInput').value = recommendedIDs.value | ||||
|     getBlocks() | ||||
|     resetCirclePickers() | ||||
| } | ||||
|  | @ -0,0 +1,35 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     detect for Circles if plaintext insert/storage is enabled | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| plaintext_enabled = null | ||||
| 
 | ||||
| fetch('/config/get/general.store_plaintext_blocks', { | ||||
|     method: 'GET', | ||||
|     headers: { | ||||
|       "token": webpass | ||||
|     }}) | ||||
| .then((resp) => resp.text()) | ||||
| .then(function(data) { | ||||
|     plaintext_enabled = true | ||||
|     if (data == "false"){ | ||||
|         plaintext_enabled = false | ||||
|         PNotify.error({ | ||||
|             text: "Plaintext storage is disabled. You will not be able to see new posts or make posts yourself" | ||||
|         }) | ||||
|     } | ||||
| }) | ||||
							
								
								
									
										196
									
								
								static-data/default-plugins/circles/web/index.html
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										196
									
								
								static-data/default-plugins/circles/web/index.html
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,196 @@ | |||
| <!DOCTYPE HTML> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
|     <meta charset='utf-8'> | ||||
|     <!--Mobile responsive--> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <title> | ||||
|         Circles | ||||
|     </title> | ||||
|     <link rel="shortcut icon" type="image/ico" href="/shared/images/favicon.ico"> | ||||
|     <link rel="stylesheet" href="/shared/main/PNotifyBrightTheme.css"> | ||||
|     <link rel="stylesheet" href="/shared/fontawesome-free-5.10.2/css/all.min.css"> | ||||
|     <link rel="stylesheet" href="/gettheme"> | ||||
|     <link rel="stylesheet" href="theme.css"> | ||||
|     <script defer src="/shared/base32.js"></script> | ||||
|     <script defer src="/shared/identicon.js"></script> | ||||
|     <script defer src="/shared/node_modules/pnotify/dist/iife/PNotify.js"></script> | ||||
|     <script defer src="/shared/node_modules/pnotify/dist/iife/PNotifyButtons.js"></script> | ||||
|     <script defer src="/shared/useridenticons.js"></script> | ||||
|     <script defer src="/shared/misc.js"></script> | ||||
|     <script defer src="/shared/navbar.js"></script> | ||||
|     <script defer src="/shared/main/apicheck.js"></script> | ||||
|     <script defer src="detect-plaintext-storage.js"></script> | ||||
|     <script defer src="sethumanreadable.js"></script> | ||||
|     <script defer src="default-circle-picker.js"></script> | ||||
|     <script defer src="sort-posts.js"></script> | ||||
|     <script defer src="board.js"></script> | ||||
|     <script defer src="autorefresh.js"></script> | ||||
|     <script defer src="popular.js"></script> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|     <nav class="navbar is-dark" role="navigation" aria-label="main navigation"> | ||||
|         <div class="navbar-brand"> | ||||
|             <a class="navbar-item idLink" href="/"> | ||||
|                 <img src="/shared/images/favicon.ico" class="navbarLogo"> | ||||
|             </a> | ||||
| 
 | ||||
|             <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" | ||||
|                 data-target="navbarBasic"> | ||||
|                 <span aria-hidden="true"></span> | ||||
|                 <span aria-hidden="true"></span> | ||||
|                 <span aria-hidden="true"></span> | ||||
|             </a> | ||||
|         </div> | ||||
| 
 | ||||
|         <div id="navbarBasic" class="navbar-menu"> | ||||
|             <div class="navbar-start"> | ||||
|                 <a class="navbar-item idLink" href="/mail/">Mail</a> | ||||
|                 <a class="navbar-item idLink" href="/friends/">Friends</a> | ||||
|                 <a class="navbar-item idLink" href="/board/">Circles</a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
|     <!--Hero (Dark Section)--> | ||||
|     <section class="hero is-small is-dark"> | ||||
|         <div class="hero-body"> | ||||
|             <div class="container"> | ||||
|                 <div class="columns"> | ||||
|                     <div class="column"> | ||||
|                         <h1 class="title"> | ||||
|                             Circles <span class="is-pulled-right">v<span id='circlesVersion'></span></span> | ||||
|                         </h1> | ||||
|                         <h2 class="subtitle"> | ||||
|                             Anonymous message boards | ||||
|                         </h2> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|     </section> | ||||
| 
 | ||||
|     <br> | ||||
| 
 | ||||
|     <!--Start Content--> | ||||
|     <div class="container"> | ||||
|         <div class="columns"> | ||||
|             <!--Add Friend--> | ||||
|             <div class="column is-one-third"> | ||||
|                 <div class="card"> | ||||
|                     <form method='POST' action='/' id='addMsg'> | ||||
|                         <header class="card-header"> | ||||
|                             <p class="card-header-title"> | ||||
|                                 Post message | ||||
|                             </p> | ||||
|                         </header> | ||||
|                         <div class="card-content"> | ||||
|                             <div class="content"> | ||||
|                                     <textarea id='newMsgText' class="textarea" name='newMsgText' rows=10 cols=50 required | ||||
|                                         minlength="2"></textarea> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <footer class="card-footer"> | ||||
|                             <a class="card-footer-item"> | ||||
|                                 <input class='button is-primary' type='submit' value='Post'> | ||||
|                             </a> | ||||
|                         </footer> | ||||
|                     </form> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <!--Feed--> | ||||
|             <div class="column"> | ||||
|                 <div class="card"> | ||||
|                     <header class="card-header"> | ||||
|                         <p class="card-header-title"> | ||||
|                             Feed | ||||
|                         </p> | ||||
|                     </header> | ||||
|                     <div class="card-content"> | ||||
|                         <div class="content"> | ||||
|                             <div class="field"> | ||||
|                                 <div class="field has-addons"> | ||||
|                                     <p class="control"> | ||||
|                                         <a class="button is-static">Circle Name</a> | ||||
|                                     </p> | ||||
|                                     <p class="control is-expanded"> | ||||
|                                         <input id="feedIDInput" class="input" placeholder="Board name" value="global"> | ||||
|                                     </p> | ||||
|                                     <p class="control"> | ||||
|                                         <a class="button is-success" id="refreshFeed">Refresh Feed</a> | ||||
|                                     </p> | ||||
|                                 </div> | ||||
|                                 <div class="field"> | ||||
|                                     <div class="columns"> | ||||
|                                         <div class="column is-2"> | ||||
|                                             <div class="control"> | ||||
|                                                 <label for="recommendedBoards" class="label">Default Circles:</label> | ||||
|                                                 <div class="select"> | ||||
|                                                     <select id="recommendedBoards"> | ||||
|                                                         <option value=""></option> | ||||
|                                                         <option value="global">Global</option> | ||||
|                                                         <option value="onionr">Onionr</option> | ||||
|                                                         <option value="games">Games</option> | ||||
|                                                         <option value="politics">Politics</option> | ||||
|                                                         <option value="tech">Tech</option> | ||||
|                                                         <option value="random">Random</option> | ||||
|                                                         <option value="privacy">Privacy</option> | ||||
|                                                     </select> | ||||
|                                                 </div> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                         <div class="column is-2"> | ||||
|                                             <div class="control"> | ||||
|                                                 <label for="popularBoards" class="label">Popular Circles:</label> | ||||
|                                                 <div class="select"> | ||||
|                                                     <select id="popularBoards"> | ||||
|                                                         <option value="">       </option> | ||||
|                                                     </select> | ||||
|                                                 </div> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                 </div> | ||||
|                                 <p class="control"> | ||||
|                                     <br> | ||||
|                                         Note: All posts in Circles are publicly accessible. | ||||
|                                 </p> | ||||
|                                 <input type="checkbox" class="checkbox" id="refreshCheckbox" checked> | ||||
|                                 <label for="refreshCheckbox">Auto refresh feed</label> | ||||
|                                 <br> | ||||
|                                 <input type="checkbox" class="checkbox" id="postAnon" checked> | ||||
|                                 <label for="postAnon">Sign posts</label> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                         <div class="content"> | ||||
|                             <span id='loadingBoard'><i class="fas fa-yin-yang fa-spin"></i></span> | ||||
|                             <div id='feed'> | ||||
|                                 <span id='none'>None yet, try refreshing 😃</span> | ||||
|                                 <!--Message Items are appended here based on template--> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <!--Template markup for Circle message--> | ||||
|     <template id="cMsgTemplate"> | ||||
|         <div class="box cMsgBox"> | ||||
|             <div class="columns"> | ||||
|                 <div class="column cMsg"> | ||||
|                     Message | ||||
|                 </div> | ||||
|                 <div class="column cAuthor is-narrow"></div> | ||||
|                 <img class="identicon image is-48x48" alt="user icon" src="/shared/images/anon.svg"> | ||||
|                 <div class="column is-narrow cMsgDate"> | ||||
|                     Date | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </template> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
							
								
								
									
										45
									
								
								static-data/default-plugins/circles/web/popular.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								static-data/default-plugins/circles/web/popular.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Load popular boards and show them in the UI. Handle selections of popular boards. | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| 
 | ||||
| fetch('/circles/getpopular/8', { | ||||
|     method: 'GET', | ||||
|     headers: { | ||||
|       "token": webpass | ||||
| }}) | ||||
| .then((popular) => popular.text()) | ||||
| .then(function(popular) { | ||||
|     var popularSelect = document.getElementById('popularBoards') | ||||
|     let boards = popular.split(',') | ||||
|     for (board of boards){ | ||||
|         let newOption = document.createElement('option') | ||||
|         if (board == ""){continue} | ||||
|         newOption.value = board | ||||
|         newOption.innerText = board.charAt(0).toUpperCase() + board.slice(1) | ||||
|         console.debug(board) | ||||
|         popularSelect.appendChild(newOption) | ||||
|     } | ||||
| }) | ||||
| 
 | ||||
| document.getElementById('popularBoards').onchange = function(){ | ||||
|     document.getElementById('feedIDInput').value = document.getElementById('popularBoards').value | ||||
|     getBlocks() | ||||
|     resetCirclePickers() | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										39
									
								
								static-data/default-plugins/circles/web/sethumanreadable.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								static-data/default-plugins/circles/web/sethumanreadable.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Set human readable public keys onto post author elements | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| humanReadableKeys = {} | ||||
| 
 | ||||
| function setHumanReadableIDOnPost(el, key){ | ||||
|     if (typeof humanReadableKeys[key] == "undefined"){ | ||||
|         fetch('/getHumanReadable/' + key, { | ||||
|             method: 'GET', | ||||
|             headers: { | ||||
|               "token": webpass | ||||
|             }}) | ||||
|         .then((resp) => resp.text()) // Transform the data into json
 | ||||
|         .then(function(data) { | ||||
|             if (data.includes('HTML')){ | ||||
|                 return | ||||
|             } | ||||
|             humanReadableKeys[key] = data | ||||
|             setHumanReadableIDOnPost(el, key) | ||||
|         }) | ||||
|         return | ||||
|     } | ||||
|     el.innerText = humanReadableKeys[key].split('-').slice(0, 3).join(' ') | ||||
| } | ||||
							
								
								
									
										30
									
								
								static-data/default-plugins/circles/web/sort-posts.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								static-data/default-plugins/circles/web/sort-posts.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Sort post entries | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| function sortEntries() { | ||||
|     var entries = document.getElementsByClassName('entry') | ||||
| 
 | ||||
|     if (entries.length > 1) { | ||||
|         const sortBy = 'timestamp' | ||||
|         const parent = entries[0].parentNode | ||||
| 
 | ||||
|         const sorted = Array.from(entries).sort((a, b) => b.getAttribute(sortBy) - a.getAttribute(sortBy)) | ||||
|         sorted.forEach(element => parent.appendChild(element)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										11
									
								
								static-data/default-plugins/circles/web/theme.css
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								static-data/default-plugins/circles/web/theme.css
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| 
 | ||||
| .cMsg{ | ||||
|     word-wrap:break-word; | ||||
|     word-break:break-word; | ||||
|     white-space: pre-wrap; | ||||
| } | ||||
| 
 | ||||
| body{ | ||||
|     background-color: #212224; | ||||
|     color: white; | ||||
| } | ||||
|  | @ -7,6 +7,7 @@ import os | |||
| 
 | ||||
| import ujson as json | ||||
| from flask import Response, request, redirect, Blueprint, abort | ||||
| from flask import send_from_directory | ||||
| import deadsimplekv as simplekv | ||||
| 
 | ||||
| from onionrusers import contactmanager | ||||
|  | @ -32,6 +33,18 @@ import sentboxdb | |||
| """ | ||||
| flask_blueprint = Blueprint('mail', __name__) | ||||
| kv = simplekv.DeadSimpleKV(identifyhome.identify_home() + '/mailcache.dat') | ||||
| root = os.path.dirname(os.path.realpath(__file__)) | ||||
| 
 | ||||
| 
 | ||||
| @flask_blueprint.route('/mail/<path:path>', endpoint='mailstatic') | ||||
| def load_mail(path): | ||||
|     return send_from_directory(root + '/web/', path) | ||||
| 
 | ||||
| 
 | ||||
| @flask_blueprint.route('/mail/', endpoint='mailindex') | ||||
| def load_mail_index(): | ||||
|     return send_from_directory(root + '/web/', 'index.html') | ||||
| 
 | ||||
| 
 | ||||
| @flask_blueprint.route('/mail/ping') | ||||
| def mail_ping(): | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ import sentboxdb, mailapi, loadinbox # import after path insert | |||
| from onblacklist import on_blacklist_add | ||||
| 
 | ||||
| flask_blueprint = mailapi.flask_blueprint | ||||
| security_whitelist = ['staticfiles.mail', 'staticfiles.mailindex'] | ||||
| security_whitelist = ['mail.mailstatic', 'mail.mailindex'] | ||||
| 
 | ||||
| 
 | ||||
| def add_deleted(keyStore, b_hash): | ||||
|  |  | |||
							
								
								
									
										28
									
								
								static-data/default-plugins/pms/web/closesettings.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								static-data/default-plugins/pms/web/closesettings.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Settings modal closing | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| document.getElementById('closeSettingsModalButton').onclick = function(){ | ||||
|     document.getElementById('settingsModal').classList.remove('is-active') | ||||
|     setActiveTab('inbox') | ||||
| } | ||||
| 
 | ||||
| document.querySelector("#settingsModal .modal-background").onclick = function(){ | ||||
|     document.getElementById('settingsModal').classList.remove('is-active') | ||||
|     setActiveTab('inbox') | ||||
| } | ||||
							
								
								
									
										261
									
								
								static-data/default-plugins/pms/web/index.html
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										261
									
								
								static-data/default-plugins/pms/web/index.html
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,261 @@ | |||
| <!DOCTYPE html> | ||||
| <html> | ||||
| 
 | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <title> | ||||
|         Onionr Mail | ||||
|     </title> | ||||
|     <link rel="shortcut icon" type="image/ico" href="/shared/images/favicon.ico"> | ||||
|     <link rel="stylesheet" href="/shared/fontawesome-free-5.10.2/css/all.min.css"> | ||||
|     <link rel="stylesheet" href="/shared/main/PNotifyBrightTheme.css"> | ||||
|     <link rel="stylesheet" href="/gettheme"> | ||||
|     <link rel="stylesheet" href="/shared/node_modules/bulma-switch/dist/css/bulma-switch.min.css"> | ||||
|     <link rel="stylesheet" href="/mail/mail.css"> | ||||
|     <script defer src="/shared/node_modules/pnotify/dist/iife/PNotify.js"></script> | ||||
|     <script defer src="/shared/node_modules/pnotify/dist/iife/PNotifyButtons.js"></script> | ||||
|     <script defer src="/shared/main/apicheck.js"></script> | ||||
|     <script defer src="/shared/misc.js"></script> | ||||
|     <script defer src="/mail/sethumanreadable.js"></script> | ||||
|     <script defer src="/mail/loadsettings.js"></script> | ||||
|     <script defer src="/mail/mail.js"></script> | ||||
|     <script defer src="/mail/sendmail.js"></script> | ||||
|     <script defer src="/mail/closesettings.js"></script> | ||||
|     <script defer src="/mail/settings.js"></script> | ||||
|     <script defer src="/shared/navbar.js"></script> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
| 
 | ||||
|     <nav class="navbar is-dark" role="navigation" aria-label="main navigation"> | ||||
|         <div class="navbar-brand"> | ||||
|             <a class="navbar-item idLink" href="/"> | ||||
|                 <img src="/shared/images/favicon.ico" class="navbarLogo"> | ||||
|             </a> | ||||
| 
 | ||||
|             <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" | ||||
|                 data-target="navbarBasic"> | ||||
|                 <span aria-hidden="true"></span> | ||||
|                 <span aria-hidden="true"></span> | ||||
|                 <span aria-hidden="true"></span> | ||||
|             </a> | ||||
|         </div> | ||||
| 
 | ||||
|         <div id="navbarBasic" class="navbar-menu"> | ||||
|             <div class="navbar-start"> | ||||
|                 <a class="navbar-item idLink" href="/mail/">Mail</a> | ||||
|                 <a class="navbar-item idLink" href="/friends/">Friends</a> | ||||
|                 <a class="navbar-item idLink" href="/board/">Circles</a> | ||||
|             </div> | ||||
|         </div> | ||||
|     </nav> | ||||
| 
 | ||||
|     <!--Hero (Dark Section)--> | ||||
|     <section class="hero is-small is-dark"> | ||||
|         <div class="hero-body"> | ||||
|             <div class="container"> | ||||
|                 <div class="columns"> | ||||
|                     <div class="column"> | ||||
|                         <h1 class="title"> | ||||
|                             Mail | ||||
|                         </h1> | ||||
|                         <h2 class="subtitle"> | ||||
|                             Private and safe messages | ||||
|                         </h2> | ||||
|                     </div> | ||||
|                     <div class="column is-7"> | ||||
|                         <div class="field"> | ||||
|                             <div class="field has-addons"> | ||||
|                                 <p class="control"> | ||||
|                                     <a class="button is-static"> | ||||
|                                         <i class="fas fa-fingerprint"></i> | ||||
|                                     </a> | ||||
|                                 </p> | ||||
|                                 <p class="control is-expanded"> | ||||
|                                     <input id="myPub" class="input myPub" type="text" readonly> | ||||
|                                 </p> | ||||
|                                 <p class="control"> | ||||
|                                     <a id="myPubCopy" class="button is-primary"><i class="fas fa-copy"></i></a> | ||||
|                                 </p> | ||||
|                             </div> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </section> | ||||
| 
 | ||||
|     <br> | ||||
|     <div id="messageDisplay" class="overlay"> | ||||
|         <div class="overlayContent"> | ||||
|             <span class="closeOverlay" overlay="messageDisplay"></span> | ||||
|             <div> | ||||
|                 From: <input type="text" id="fromUser" readonly> Signature: <span id="sigValid"></span> <span | ||||
|                     id="addUnknownContact"><button class="button is-primary">Add to Contacts</button></span> | ||||
|             </div> | ||||
|             <div class="break-up"> | ||||
|                 Subject: <span id="subjectView"></span> | ||||
|             </div> | ||||
|             <div> | ||||
|                 <button id="replyBtn" class="button is-primary break-up">Reply</button> | ||||
|             </div> | ||||
|             <div id="signatureValidity"></div> | ||||
|             <div id="threadDisplay" class="pre messageContent"> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div id="sentboxDisplay" class="overlay"> | ||||
|         <div class="overlayContent"> | ||||
|             <span class="closeOverlay" overlay="sentboxDisplay"></span> | ||||
|             To: <input id="toID" readonly type="text"> | ||||
|             <div id="sentboxDisplayText" class="pre messageContent"> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| 
 | ||||
|     <div id="settingsModal" class="modal"> | ||||
|         <div class="modal-background"></div> | ||||
|         <div class="modal-card"> | ||||
|           <header class="modal-card-head"> | ||||
|             <button id="closeSettingsModalButton" class="closeSettingsModal delete" aria-label="close"></button> | ||||
|           </header> | ||||
|           <section class="modal-card-body"> | ||||
|               <div class="columns"> | ||||
|                   <div class="column"> | ||||
|                       <div>Ask senders to use forward-secrecy</div> | ||||
|                       <small>Turn off if using 1 ID on more than 1 device, or have Onionr data erasure on exit enabled.</small> | ||||
|                   </div> | ||||
|                   <div class="column is-2"> | ||||
|                     <div class="field"> | ||||
|                         <input id="forwardSecrecySetting" type="checkbox" | ||||
|                         class="switch is-rounded is-danger" checked> | ||||
|                         <label for="forwardSecrecySetting"></label> | ||||
|                     </div> | ||||
|                   </div> | ||||
|               </div> | ||||
|             <div class="columns"> | ||||
|                 <div class="column"> | ||||
|                     <div>Message padding</div> | ||||
|                     <small>Improves privacy slightly for a small performance hit.</small> | ||||
|                 </div> | ||||
|                 <div class="column is-2"> | ||||
|                   <div class="field"> | ||||
|                       <input id="messagePaddingSetting" type="checkbox" | ||||
|                       class="switch is-rounded is-warning"> | ||||
|                       <label for="messagePaddingSetting"></label> | ||||
|                   </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="columns"> | ||||
|                 <div class="column"> | ||||
|                     Inbox notifications | ||||
|                 </div> | ||||
|                 <div class="column is-2"> | ||||
|                     <div class="field"> | ||||
|                         <input id="notificationSetting" type="checkbox" | ||||
|                         class="switch is-rounded" checked> | ||||
|                         <label for="notificationSetting"></label> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="columns notificationSetting"> | ||||
|                 <div class="column"> | ||||
|                     Notifications for stranger's messages | ||||
|                 </div> | ||||
|                 <div class="column is-2"> | ||||
|                     <div class="field"> | ||||
|                         <input id="strangersNotification" type="checkbox" | ||||
|                         class="switch is-rounded" checked> | ||||
|                         <label for="strangersNotification"></label> | ||||
|                     </div> | ||||
|                 </div> | ||||
|             </div> | ||||
|             <div class="columns notificationSetting"> | ||||
|                 <div class="column"> | ||||
|                     Notification sound | ||||
|                 </div> | ||||
|                 <div class="column is-2"> | ||||
|                   <div class="field"> | ||||
|                     <input id="notificationSound" type="checkbox" | ||||
|                     class="switch is-rounded" checked> | ||||
|                     <label for="notificationSound"></label> | ||||
|                   </div> | ||||
|                 </div> | ||||
|               </div> | ||||
|               Signature | ||||
|               <textarea id="mailSignatureSetting" class="textarea" placeholder="Signature to add to every message" rows="5"></textarea> | ||||
|           </section> | ||||
|         </div> | ||||
|       </div> | ||||
|     <!--Start of content--> | ||||
|     <div class="container"> | ||||
|         <div class="tabs" id="tabBtns"> | ||||
|             <ul> | ||||
|                 <li class="is-active"> | ||||
|                     <a> | ||||
|                         <span class="icon"> | ||||
|                             <i class="fa fa-envelope"></i> | ||||
|                         </span> | ||||
|                         <span id="inboxTab">Inbox</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a> | ||||
|                         <span class="icon"> | ||||
|                             <i class="fa fa-paper-plane"></i> | ||||
|                         </span> | ||||
|                         <span>Sent</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a> | ||||
|                         <span class="icon"> | ||||
|                             <i class="fa fa-pen"></i> | ||||
|                         </span> | ||||
|                         <span>Compose</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|                 <li> | ||||
|                     <a> | ||||
|                         <span class="icon"> | ||||
|                             <i class="fa fa-cog"></i> | ||||
|                         </span> | ||||
|                         <span>Settings</span> | ||||
|                     </a> | ||||
|                 </li> | ||||
|             </ul> | ||||
|         </div> | ||||
|         <div class="content" id="noInbox">No messages to show ¯\_(ツ)_/¯</div> | ||||
|         <div class="content"> | ||||
|             <div class="mailPing"> | ||||
|                 API server either shutdown, has disabled mail, or has experienced a bug. | ||||
|             </div> | ||||
|             <div id="threads" class="threads"> | ||||
|                 <div id="threadPlaceholder">Nothing here yet 😞</div> | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div id="sendMessage"> | ||||
|         <div class="container"> | ||||
|             <div class="field"> | ||||
|                 <label><i class="fas fa-user"></i> Select friend: <select id="friendSelect"></select></label> | ||||
|             </div> | ||||
|             <form method="post" action="" id="sendForm" enctype="application/x-www-form-urlencoded"> | ||||
|                 <div class="field"> | ||||
|                     To: <input id="draftID" type="text" name="to" placeholder="pubkey or select above" required> | ||||
|                 </div> | ||||
|                 Subject: <input name="subject" id="draftSubject" maxlength="25" type="text" | ||||
|                     placeholder="message subject"> | ||||
|                 <div class="field"> | ||||
|                     <textarea name="message" class="textarea" placeholder="type your message..." id="draftText" required></textarea> | ||||
|                 </div> | ||||
|                 <input type="submit" value="Send" class="button is-primary successBtn"> | ||||
|             </form> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div id="infoOverlay" class="overlay"> | ||||
|     </div> | ||||
| </body> | ||||
| 
 | ||||
| </html> | ||||
							
								
								
									
										29
									
								
								static-data/default-plugins/pms/web/loadsettings.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								static-data/default-plugins/pms/web/loadsettings.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| mailSettings = {} | ||||
| 
 | ||||
| fetch('/config/get/mail', { | ||||
|     headers: { | ||||
|       "content-type": "application/json", | ||||
|       "token": webpass | ||||
|     }}) | ||||
| .then((resp) => resp.json()) | ||||
| .then(function(settings) { | ||||
|     mailSettings = settings || {} | ||||
|     if (mailSettings.default_forward_secrecy === false){ | ||||
|         document.getElementById('forwardSecrecySetting').checked = false | ||||
|     } | ||||
|     if (mailSettings.use_padding === false){ | ||||
|       document.getElementById('messagePaddingSetting').checked = false | ||||
|     } | ||||
|     if (mailSettings.notificationSetting === false){ | ||||
|       document.getElementById('notificationSetting').checked = false | ||||
|     } | ||||
|     if (mailSettings.notificationSound === false){ | ||||
|       document.getElementById('notificationSound').checked = false | ||||
|     } | ||||
|     if (typeof mailSettings.signature != undefined && mailSettings.signature != null && mailSettings.signature != ""){ | ||||
|       document.getElementById('mailSignatureSetting').value = mailSettings.signature | ||||
|     } | ||||
|     if (mailSettings.strangersNotification == false){ | ||||
|       document.getElementById('strangersNotification').checked = false | ||||
|     } | ||||
|   }) | ||||
							
								
								
									
										60
									
								
								static-data/default-plugins/pms/web/mail.css
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										60
									
								
								static-data/default-plugins/pms/web/mail.css
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| .threadEntry button{ | ||||
|     margin-right: 1%; | ||||
| } | ||||
| .threadEntry span, .sentboxList span{ | ||||
|     padding-left: 1%; | ||||
| } | ||||
| .threadEntry, .sentboxList{ | ||||
|     cursor: pointer; | ||||
| } | ||||
| 
 | ||||
| .overlayContent{ | ||||
|     background-color: lightgray; | ||||
|     border: 3px solid black; | ||||
|     border-radius: 3px; | ||||
|     color: black; | ||||
|     font-family: Verdana, Geneva, Tahoma, sans-serif; | ||||
|     min-height: 100%; | ||||
|     padding: 1em; | ||||
|     margin: 1em; | ||||
| } | ||||
| 
 | ||||
| #draftText{ | ||||
|     margin-top: 1em; | ||||
|     margin-bottom: 1em; | ||||
|     display: block; | ||||
|     width: 50%; | ||||
|     height: 75%; | ||||
|     min-width: 2%; | ||||
|     min-height: 5%; | ||||
|     background: white; | ||||
|     color: black; | ||||
| } | ||||
| 
 | ||||
| .sentboxList{ | ||||
|     padding-top: 1em; | ||||
| } | ||||
| 
 | ||||
| .messageContent{ | ||||
|     padding-top: 1em; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #tabBtns ul .icon { | ||||
|     pointer-events: none; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #settingsModal .modal-card-body{ | ||||
|     font-size: 150%; | ||||
|     min-height: 300px; | ||||
| } | ||||
| 
 | ||||
| #settingsModal textarea{ | ||||
|     resize: none; | ||||
|     margin-top: 1em; | ||||
| } | ||||
| 
 | ||||
| #settingsModal small{ | ||||
|     font-size: 0.5em; | ||||
| } | ||||
							
								
								
									
										450
									
								
								static-data/default-plugins/pms/web/mail.js
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										450
									
								
								static-data/default-plugins/pms/web/mail.js
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,450 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     This file handles the mail interface | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| pms = '' | ||||
| sentbox = '' | ||||
| threadPart = document.getElementById('threads') | ||||
| threadPlaceholder = document.getElementById('threadPlaceholder') | ||||
| tabBtns = document.getElementById('tabBtns') | ||||
| threadContent = {} | ||||
| replyBtn = document.getElementById('replyBtn') | ||||
| addUnknownContact = document.getElementById('addUnknownContact') | ||||
| noInbox = document.getElementById('noInbox') | ||||
| humanReadableCache = {} | ||||
| 
 | ||||
| function stripEndZeroes(str){ | ||||
|     str = str.split("") | ||||
|     let zeroCount = 0 | ||||
|     for (x = str.length - 1; x != 0; x--){ | ||||
|         if (str[x] == "0"){ | ||||
|             zeroCount += 1 | ||||
|         } | ||||
|         else{ | ||||
|             break | ||||
|         } | ||||
|     } | ||||
|     str.splice(str.length - zeroCount, zeroCount) | ||||
|     str = str.join("") | ||||
|     return str | ||||
| } | ||||
| 
 | ||||
| async function addContact(pubkey, friendName){ | ||||
|     fetch('/friends/add/' + pubkey, { | ||||
|         method: 'POST', | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|         }}).then(function(data) { | ||||
|             if (friendName.trim().length > 0){ | ||||
|                 post_to_url('/friends/setinfo/' + pubkey + '/name', {'data': friendName, 'token': webpass}) | ||||
|             } | ||||
|         }) | ||||
| } | ||||
| 
 | ||||
| function openReply(bHash, quote, subject){ | ||||
|     var inbox = document.getElementsByClassName('threadEntry') | ||||
|     var entry = '' | ||||
|     var friendName = '' | ||||
|     var key = '' | ||||
|     for(var i = 0; i < inbox.length; i++) { | ||||
|         if (inbox[i].getAttribute('data-hash') === bHash){ | ||||
|             entry = inbox[i] | ||||
|         } | ||||
|     } | ||||
|     if (entry.getAttribute('data-nameset') == 'true'){ | ||||
|         document.getElementById('friendSelect').value = document.getElementById('fromUser').value | ||||
|     } | ||||
|     key = entry.getAttribute('data-pubkey') | ||||
|     document.getElementById('draftID').value = key | ||||
|     document.getElementById('draftSubject').value = 'RE: ' + subject | ||||
| 
 | ||||
|     // Add quoted reply
 | ||||
|     var splitQuotes = quote.split('\n') | ||||
|     for (var x = 0; x < splitQuotes.length; x++){ | ||||
|         splitQuotes[x] = '> ' + splitQuotes[x] | ||||
|     } | ||||
| 
 | ||||
|     if (typeof humanReadableCache[key] != 'undefined'){ | ||||
|         document.getElementById('draftID').value = humanReadableCache[key] | ||||
|         quote = '\n' + humanReadableCache[key].split('-').slice(0,3).join('-') + ' wrote:\n' + splitQuotes.join('\n') | ||||
|     } | ||||
|     else{ | ||||
|         quote = '\n' + key.substring(0, 12) + ' wrote:' + '\n' + splitQuotes.join('\n') | ||||
|     } | ||||
|     document.getElementById('draftText').value = quote | ||||
|     setActiveTab('compose') | ||||
| } | ||||
| 
 | ||||
| function openThread(bHash, sender, date, sigBool, pubkey, subjectLine){ | ||||
|     addUnknownContact.style.display = 'none' | ||||
|     var messageDisplay = document.getElementById('threadDisplay') | ||||
| 
 | ||||
|     fetch('/getblockbody/' + bHash, { | ||||
|         "method": "get", | ||||
|         headers: { | ||||
|             "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.text()) | ||||
|     .then(function(resp) { | ||||
|         document.getElementById('fromUser').value = sender || 'Anonymous' | ||||
|         document.getElementById('fromUser').value = pubkey || '' | ||||
|         document.getElementById('subjectView').innerText = subjectLine | ||||
| 
 | ||||
|         resp = stripEndZeroes(resp) | ||||
| 
 | ||||
|         messageDisplay.innerText = resp | ||||
|         var sigEl = document.getElementById('sigValid') | ||||
|         var sigMsg = 'signature' | ||||
| 
 | ||||
|         // show add unknown contact button if peer is unknown
 | ||||
|         if (sender !== myPub && sigBool){ | ||||
|             addUnknownContact.style.display = 'inline' | ||||
|         } | ||||
| 
 | ||||
|         if (sigBool){ | ||||
|             sigMsg = 'Good ' + sigMsg | ||||
|             sigEl.classList.remove('danger') | ||||
|         } | ||||
|         else{ | ||||
|             sigMsg = 'Bad/no ' + sigMsg + ' (message could be impersonating someone)' | ||||
|             sigEl.classList.add('danger') | ||||
|             replyBtn.style.display = 'none' | ||||
|         } | ||||
|         sigEl.innerText = sigMsg | ||||
|         overlay('messageDisplay') | ||||
|         replyBtn.onclick = function(){ | ||||
|             document.getElementById('messageDisplay').style.visibility = 'hidden' | ||||
|             openReply(bHash, messageDisplay.innerText, subjectLine) | ||||
|         } | ||||
|         addUnknownContact.onclick = function(){ | ||||
|             var friendName = prompt("Enter an alias for this contact:") | ||||
|             if (friendName === null || friendName.length == 0){ | ||||
|                 return | ||||
|             } | ||||
|             addContact(pubkey, friendName) | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| function setActiveTab(tabName){ | ||||
|     threadPart.innerHTML = "" | ||||
|     noInbox.style.display = 'none' | ||||
|     window.inboxActive = false | ||||
|     document.getElementById('sendMessage').classList.add('is-hidden') | ||||
|     switch(tabName){ | ||||
|         case 'inbox': | ||||
|             window.inboxActive = true | ||||
|             refreshPms() | ||||
|             getInbox() | ||||
|             break | ||||
|         case 'sent': | ||||
|             getSentbox() | ||||
|             break | ||||
|         case 'compose': | ||||
|             document.getElementById('sendMessage').classList.remove('is-hidden') | ||||
|             break | ||||
|         case 'settings': | ||||
|             document.getElementById('settingsModal').classList.add('is-active') | ||||
|             break | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function deleteMessage(bHash){ | ||||
|     fetch('/mail/deletemsg/' + bHash, { | ||||
|         "method": "post", | ||||
|         headers: { | ||||
|             "token": webpass | ||||
|         }}) | ||||
| } | ||||
| 
 | ||||
| function mailPing(){ | ||||
|     fetch('/mail/ping', { | ||||
|         "method": "get", | ||||
|         headers: { | ||||
|             "token": webpass | ||||
|         }}) | ||||
|     .then(function(resp) { | ||||
|         var pings = document.getElementsByClassName('mailPing') | ||||
|         if (resp.ok){ | ||||
|             for (var i=0; i < pings.length; i++){ | ||||
|                 pings[i].style.display = 'none'; | ||||
|             } | ||||
|         } | ||||
|         else{ | ||||
|             for (var i=0; i < pings.length; i++){ | ||||
|                 pings[i].style.display = 'block'; | ||||
|             } | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| function loadInboxEntries(bHash){ | ||||
|     fetch('/getblockheader/' + bHash, { | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.json()) // Transform the data into json
 | ||||
|     .then(function(resp) { | ||||
|         //console.log(resp)
 | ||||
|         var entry = document.createElement('div') | ||||
|         var bHashDisplay = document.createElement('span') | ||||
|         //var senderInput = document.createElement('input')
 | ||||
|         var senderInput = document.createElement('span') | ||||
|         var subjectLine = document.createElement('span') | ||||
|         var dateStr = document.createElement('span') | ||||
|         var validSig = document.createElement('span') | ||||
|         var deleteBtn = document.createElement('button') | ||||
|         var humanDate = new Date(0) | ||||
|         var metadata = resp['metadata'] | ||||
|         humanDate.setUTCSeconds(resp['meta']['time']) | ||||
|         humanDate = humanDate.toString() | ||||
|         validSig.style.display = 'none' | ||||
| 
 | ||||
|         if (typeof resp['meta']['signer'] != 'undefined' && resp['meta']['signer'] != ''){ | ||||
|             fetch('/friends/getinfo/' + resp['meta']['signer'] + '/name', { | ||||
|                 headers: { | ||||
|                   "token": webpass | ||||
|                 }}) | ||||
|             .then(function(resp2){ | ||||
|                 if (!resp2.ok){ | ||||
|                     setHumanReadableValue(senderInput, resp['meta']['signer']) | ||||
|                     entry.setAttribute('data-nameSet', false) | ||||
|                 } | ||||
|                 else{ | ||||
|                     resp2.text().then(function(resp2){ | ||||
|                     loadHumanReadableToCache(resp['meta']['signer']) | ||||
|                     senderInput.innerText = resp2 | ||||
|                     entry.setAttribute('data-nameSet', true) | ||||
|                     }) | ||||
|                 } | ||||
|             }) | ||||
| 
 | ||||
|         } | ||||
|         else{ | ||||
|             senderInput.innerText = 'Anonymous' | ||||
|             entry.setAttribute('data-nameSet', false) | ||||
|         } | ||||
|         if (! resp['meta']['validSig']){ | ||||
|             validSig.style.display = 'inline' | ||||
|             validSig.innerText = 'Signature Validity: Bad' | ||||
|             validSig.style.color = 'red' | ||||
|         } | ||||
|         //bHashDisplay.innerText = bHash.substring(0, 10)
 | ||||
|         entry.setAttribute('data-hash', bHash) | ||||
|         entry.setAttribute('data-pubkey', resp['meta']['signer']) | ||||
|         senderInput.readOnly = true | ||||
|         dateStr.innerText = humanDate.substring(0, humanDate.indexOf('(')) | ||||
|         deleteBtn.classList.add('delete', 'deleteBtn') | ||||
|         if (metadata['subject'] === undefined || metadata['subject'] === null) { | ||||
|             subjectLine.innerText = '()' | ||||
|         } | ||||
|         else{ | ||||
|             subjectLine.innerText = '(' + metadata['subject'] + ')' | ||||
|         } | ||||
|         //entry.innerHTML = 'sender ' + resp['meta']['signer'] + ' - ' + resp['meta']['time']
 | ||||
|         threadPart.appendChild(entry) | ||||
|         entry.appendChild(deleteBtn) | ||||
|         entry.appendChild(bHashDisplay) | ||||
|         entry.appendChild(senderInput) | ||||
|         entry.appendChild(subjectLine) | ||||
|         entry.appendChild(dateStr) | ||||
|         entry.appendChild(validSig) | ||||
|         entry.classList.add('threadEntry') | ||||
| 
 | ||||
|         entry.onclick = function(event){ | ||||
|             if (event.target.classList.contains('deleteBtn')){ | ||||
|                 return | ||||
|             } | ||||
|             openThread(entry.getAttribute('data-hash'), senderInput.innerText, dateStr.innerText, resp['meta']['validSig'], entry.getAttribute('data-pubkey'), subjectLine.innerText) | ||||
|         } | ||||
| 
 | ||||
|         deleteBtn.onclick = function(){ | ||||
|             entry.parentNode.removeChild(entry); | ||||
|             deleteMessage(entry.getAttribute('data-hash')) | ||||
|         } | ||||
| 
 | ||||
|       }.bind(bHash)) | ||||
| } | ||||
| 
 | ||||
| function getInbox(){ | ||||
|     if (! window.inboxActive){ | ||||
|         return | ||||
|     } | ||||
|     var els = document.getElementsByClassName('threadEntry') | ||||
|     var showed = false | ||||
|     for(var i = 0; i < pms.length; i++) { | ||||
|         var add = true | ||||
|         if (pms[i].trim().length == 0){ | ||||
|             noInbox.style.display = 'block' | ||||
|             continue | ||||
|         } | ||||
|         else{ | ||||
|             threadPlaceholder.style.display = 'none' | ||||
|             showed = true | ||||
|         } | ||||
|         for (var x = 0; x < els.length; x++){ | ||||
|             if (pms[i] === els[x].getAttribute('data-hash')){ | ||||
|                 add = false | ||||
|             } | ||||
|         } | ||||
|         if (add && window.inboxActive) { | ||||
|             loadInboxEntries(pms[i]) | ||||
|         } | ||||
|     } | ||||
|     if (! showed){ | ||||
|         threadPlaceholder.style.display = 'block' | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| function getSentbox(){ | ||||
|     fetch('/mail/getsentbox', { | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.json()) // Transform the data into json
 | ||||
|     .then(function(resp) { | ||||
|         var keys = []; | ||||
|         var entry = document.createElement('div') | ||||
|         for(var k in resp) keys.push(k); | ||||
|         if (keys.length == 0){ | ||||
|             threadPart.innerHTML = "nothing to show here yet." | ||||
|         } | ||||
|         for (var i = keys.length - 1; i > -1; i--) (function(i, resp){ | ||||
|             var entry = document.createElement('div') | ||||
|             var toLabel = document.createElement('span') | ||||
|             toLabel.innerText = 'To: ' | ||||
|             var toEl = document.createElement('span') | ||||
|             toEl.classList.add('toElement') | ||||
|             var sentDate = document.createElement('span') | ||||
|             var humanDate = new Date(0) | ||||
|             humanDate.setUTCSeconds(resp[i]['date']) | ||||
|             humanDate = humanDate.toString() | ||||
|             var preview = document.createElement('span') | ||||
|             var deleteBtn = document.createElement('button') | ||||
|             var message = resp[i]['message'] | ||||
|             deleteBtn.classList.add('deleteBtn', 'delete') | ||||
| 
 | ||||
|             sentDate.innerText = humanDate.substring(0, humanDate.indexOf('(')) | ||||
|             if (resp[i]['name'] == null || resp[i]['name'].toLowerCase() == 'anonymous'){ | ||||
|                 toEl.innerText = resp[i]['peer'] | ||||
|                 setHumanReadableValue(toEl, resp[i]['peer']) | ||||
|             } | ||||
|             else{ | ||||
|                 toEl.innerText = resp[i]['name'] | ||||
|             } | ||||
|             preview.innerText = '(' + resp[i]['subject'] + ')' | ||||
|             entry.classList.add('sentboxList') | ||||
|             entry.setAttribute('data-hash', resp[i]['hash']) | ||||
|             entry.appendChild(deleteBtn) | ||||
|             entry.appendChild(toLabel) | ||||
|             entry.appendChild(toEl) | ||||
|             entry.appendChild(preview) | ||||
|             entry.appendChild(sentDate) | ||||
| 
 | ||||
|             threadPart.appendChild(entry) | ||||
| 
 | ||||
|             entry.onclick = function(e){ | ||||
|                 if (e.target.classList.contains('deleteBtn')){ | ||||
|                     deleteMessage(e.target.parentNode.getAttribute('data-hash')) | ||||
|                     e.target.parentNode.parentNode.removeChild(e.target.parentNode) | ||||
|                     return | ||||
|                 } | ||||
|                 showSentboxWindow(toEl.innerText, message) | ||||
|             } | ||||
|         })(i, resp) | ||||
|         threadPart.appendChild(entry) | ||||
|       }.bind(threadPart)) | ||||
| } | ||||
| 
 | ||||
| function showSentboxWindow(to, content){ | ||||
|     content = stripEndZeroes(content) | ||||
|     document.getElementById('toID').value = to | ||||
|     document.getElementById('sentboxDisplayText').innerText = content | ||||
|     overlay('sentboxDisplay') | ||||
| } | ||||
| 
 | ||||
| function refreshPms(callNext){ | ||||
|     if (! window.inboxActive){ | ||||
|         return | ||||
|     } | ||||
| fetch('/mail/getinbox', { | ||||
|     headers: { | ||||
|       "token": webpass | ||||
|     }}) | ||||
| .then((resp) => resp.text()) | ||||
| .then(function(data) { | ||||
|     pms = data.split(',') | ||||
|     if (pms.length > 0){ | ||||
|         noInbox.style.display = 'none' | ||||
|     } | ||||
|     if (callNext){ | ||||
|         getInbox() | ||||
|     } | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| tabBtns.onclick = function(event){ | ||||
|     var children = tabBtns.children[0].children | ||||
|     for (var i = 0; i < children.length; i++) { | ||||
|         var btn = children[i] | ||||
|         btn.classList.remove('is-active') | ||||
|     } | ||||
|     event.target.parentElement.parentElement.classList.add('is-active') | ||||
|     setActiveTab(event.target.innerText.toLowerCase()) | ||||
| } | ||||
| 
 | ||||
| for (var i = 0; i < document.getElementsByClassName('refresh').length; i++){ | ||||
|     document.getElementsByClassName('refresh')[i].style.float = 'right' | ||||
| } | ||||
| 
 | ||||
| fetch('/friends/list', { | ||||
|     headers: { | ||||
|       "token": webpass | ||||
|     }}) | ||||
| .then((resp) => resp.json()) // Transform the data into json
 | ||||
| .then(function(resp) { | ||||
|     var friendSelectParent = document.getElementById('friendSelect') | ||||
|     var keys = []; | ||||
|     for(var k in resp) keys.push(k); | ||||
| 
 | ||||
|     friendSelectParent.appendChild(document.createElement('option')) | ||||
|     for (var i = 0; i < keys.length; i++) { | ||||
|         var option = document.createElement("option") | ||||
|         var name = resp[keys[i]]['name'] || "" | ||||
|         option.value = keys[i] | ||||
|         if (name.length == 0){ | ||||
|             option.text = keys[i] | ||||
|         } | ||||
|         else{ | ||||
|             option.text = name | ||||
|         } | ||||
|         friendSelectParent.appendChild(option) | ||||
|     } | ||||
| }) | ||||
| setActiveTab('inbox') | ||||
| 
 | ||||
| setInterval(function(){mailPing()}, 10000) | ||||
| mailPing() | ||||
| window.inboxInterval = setInterval(function(){refreshPms(true)}, 3000) | ||||
| refreshPms(true) | ||||
| 
 | ||||
| document.addEventListener("visibilitychange", function() { | ||||
|     if (document.visibilityState === 'visible') { | ||||
|         refreshPms() | ||||
|     } | ||||
|   }) | ||||
							
								
								
									
										95
									
								
								static-data/default-plugins/pms/web/sendmail.js
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								static-data/default-plugins/pms/web/sendmail.js
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,95 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     This file handles the mail interface | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| 
 | ||||
| var sendbutton = document.getElementById('sendMail') | ||||
| messageContent = document.getElementById('draftText') | ||||
| to = document.getElementById('draftID') | ||||
| subject = document.getElementById('draftSubject') | ||||
| friendPicker = document.getElementById('friendSelect') | ||||
| 
 | ||||
| function utf8Length(s) { | ||||
|     var size = encodeURIComponent(s).match(/%[89ABab]/g); | ||||
|     return s.length + (size ? size.length : 0); | ||||
|   } | ||||
| 
 | ||||
| function padString(string_data, round_nearest_byte_exponent = 3){ | ||||
|     if (utf8Length(string_data) === 0){ | ||||
|         string_data += '0' | ||||
|     } | ||||
|     let round_size = 10 ** round_nearest_byte_exponent | ||||
|     while (utf8Length(string_data) % round_size > 0){ | ||||
|         string_data += '0' | ||||
|     } | ||||
|     return string_data | ||||
| } | ||||
| 
 | ||||
| function sendMail(toData, message, subject){ | ||||
|     let meta = {'subject': subject} | ||||
| 
 | ||||
|     if (document.getElementById('messagePaddingSetting').checked){ | ||||
|         message = padString(message) | ||||
|     } | ||||
| 
 | ||||
|     if (document.getElementById('mailSignatureSetting').value !== false){ | ||||
|         message += "\n" | ||||
|         message += document.getElementById('mailSignatureSetting').value | ||||
|     } | ||||
| 
 | ||||
|     postData = {'message': message, 'to': toData, 'type': 'pm', 'encrypt': true, 'meta': JSON.stringify(meta)} | ||||
|     postData.forward = document.getElementById('forwardSecrecySetting').checked | ||||
|     postData = JSON.stringify(postData) | ||||
|     sendForm.style.display = 'none' | ||||
| 
 | ||||
|     fetch('/insertblock', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.text()) // Transform the data into text
 | ||||
|     .then(function(data) { | ||||
|         sendForm.style.display = 'block' | ||||
|         PNotify.success({ | ||||
|             text: 'Queued for sending!', | ||||
|             delay: 3500, | ||||
|             mouseReset: false | ||||
|         }) | ||||
|         to.value = subject.value = messageContent.value = "" | ||||
|         friendPicker.value = "" | ||||
|         subject.value = "" | ||||
|       }) | ||||
| } | ||||
| 
 | ||||
| var friendPicker = document.getElementById('friendSelect') | ||||
| friendPicker.onchange = function(){ | ||||
|     to.value = friendPicker.value | ||||
| } | ||||
| 
 | ||||
| sendForm.onsubmit = function(){ | ||||
|     if (! to.value.includes("-") && to.value.length !== 56 && to.value.length !== 52){ | ||||
|         PNotify.error({ | ||||
|             text: 'User ID is not valid' | ||||
|         }) | ||||
|     } | ||||
|     else{ | ||||
|         sendMail(to.value, messageContent.value, subject.value) | ||||
|     } | ||||
|     return false | ||||
| } | ||||
							
								
								
									
										40
									
								
								static-data/default-plugins/pms/web/sethumanreadable.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								static-data/default-plugins/pms/web/sethumanreadable.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Load human readable public keys into a cache | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| function loadHumanReadableToCache(key){ | ||||
|     fetch('/getHumanReadable/' + key, { | ||||
|         headers: { | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.text()) | ||||
|     .then(function(resp) { | ||||
|         humanReadableCache[key] = resp | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| function setHumanReadableValue(el, key){ | ||||
|     if (typeof humanReadableCache[key] != 'undefined'){ | ||||
|         el.innerText = humanReadableCache[key].split('-').slice(0,3).join('-') | ||||
|         return | ||||
|     } | ||||
|     else{ | ||||
|         loadHumanReadableToCache(key) | ||||
|         setTimeout(function(){setHumanReadableValue(el, key)}, 100) | ||||
|         return | ||||
|     } | ||||
| } | ||||
							
								
								
									
										120
									
								
								static-data/default-plugins/pms/web/settings.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								static-data/default-plugins/pms/web/settings.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,120 @@ | |||
| /* | ||||
|     Onionr - Private P2P Communication | ||||
| 
 | ||||
|     Handle mail settings | ||||
| 
 | ||||
|     This program is free software: you can redistribute it and/or modify | ||||
|     it under the terms of the GNU General Public License as published by | ||||
|     the Free Software Foundation, either version 3 of the License, or | ||||
|     (at your option) any later version. | ||||
| 
 | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
| 
 | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
| */ | ||||
| var notificationSetting = document.getElementById('notificationSetting') | ||||
| var friendOnlyNotification = document.getElementById('strangersNotification') | ||||
| var notificationSound = document.getElementById('notificationSound') | ||||
| var sigSetting = document.getElementById('mailSignatureSetting') | ||||
| 
 | ||||
| document.getElementById('forwardSecrecySetting').onchange = function(e){ | ||||
|     postData = JSON.stringify({"default_forward_secrecy": e.target.checked}) | ||||
|     fetch('/config/set/mail', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then((resp) => resp.text()) | ||||
|     .then(function(data) { | ||||
|         mailSettings['forwardSecrecy'] = document.getElementById('forwardSecrecySetting').checked | ||||
|         PNotify.success({ | ||||
|             text: 'Successfully toggled default forward secrecy' | ||||
|         }) | ||||
|       }) | ||||
| } | ||||
| 
 | ||||
| notificationSound.onchange = function(e){ | ||||
|     var postData = JSON.stringify({"notificationSound": e.target.checked}) | ||||
|     fetch('/config/set/mail', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then(function(data) { | ||||
|         mailSettings['notificationSound'] = notificationSound.checked | ||||
|         PNotify.success({ | ||||
|             text: 'Successfully notification sound' | ||||
|         }) | ||||
|       }) | ||||
| } | ||||
| 
 | ||||
| friendOnlyNotification.onchange = function(e){ | ||||
|     var postData = JSON.stringify({"strangersNotification": e.target.checked}) | ||||
|     fetch('/config/set/mail', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then(function(data) { | ||||
|         mailSettings['strangersNotification'] = friendOnlyNotification.checked | ||||
|         PNotify.success({ | ||||
|             text: 'Successfully toggled notifications from strangers' | ||||
|         }) | ||||
|       }) | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| notificationSetting.onchange = function(e){ | ||||
|     var notificationSettings = document.getElementsByClassName('notificationSetting') | ||||
|     if (e.target.checked){ | ||||
|         for (i = 0; i < notificationSettings.length; i++){ | ||||
|             notificationSettings[i].style.display = "flex" | ||||
|         } | ||||
|     } | ||||
|     else{ | ||||
|         for (i = 0; i < notificationSettings.length; i++){ | ||||
|             notificationSettings[i].style.display = "none" | ||||
|         } | ||||
|     } | ||||
|     var postData = JSON.stringify({"notificationSetting": e.target.checked}) | ||||
|     fetch('/config/set/mail', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then(function(data) { | ||||
|         mailSettings['notificationSetting'] = notificationSetting.checked | ||||
|         PNotify.success({ | ||||
|             text: 'Successfully toggled default mail notifications' | ||||
|         }) | ||||
|       }) | ||||
| } | ||||
| 
 | ||||
| sigSetting.onchange = function(){ | ||||
|     var postData = JSON.stringify({"signature": sigSetting.value}) | ||||
|     fetch('/config/set/mail', { | ||||
|         method: 'POST', | ||||
|         body: postData, | ||||
|         headers: { | ||||
|           "content-type": "application/json", | ||||
|           "token": webpass | ||||
|         }}) | ||||
|     .then(function(data) { | ||||
|         mailSettings['signature'] = sigSetting.value | ||||
|         PNotify.success({ | ||||
|             text: 'Set mail signature' | ||||
|         }) | ||||
|       }) | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue