Compare commits
10 Commits
Author | SHA1 | Date |
---|---|---|
quenousimporte | c455c721d0 | |
quenousimporte | e5a50babf7 | |
quenousimporte | 0f1c4407c3 | |
quenousimporte | 309354c43d | |
quenousimporte | cc792e10fd | |
quenousimporte | d77defe68e | |
quenousimporte | 87a2cbc949 | |
quenousimporte | 77489ca4ab | |
quenousimporte | 852edd3600 | |
quenousimporte | a2ca263fe7 |
174
cli/app.py
174
cli/app.py
|
@ -1,174 +0,0 @@
|
||||||
import io
|
|
||||||
import json
|
|
||||||
import subprocess
|
|
||||||
import urllib.parse
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import re
|
|
||||||
import shutil
|
|
||||||
|
|
||||||
def filename(note):
|
|
||||||
return re.sub(r'[\?\"<>|\*:\/\\]', '_', note["title"]) + ".md"
|
|
||||||
|
|
||||||
def listnotes(filter = "", checkcontent = False):
|
|
||||||
matching = []
|
|
||||||
for i in reversed(range(len(data))):
|
|
||||||
|
|
||||||
if filter.lower() in data[i]["title"].lower():
|
|
||||||
print("[" + str(i) + "]", data[i]["title"])
|
|
||||||
matching.append(data[i])
|
|
||||||
|
|
||||||
elif checkcontent and filter.lower() in data[i]["content"].lower():
|
|
||||||
print("[" + str(i) + "]", data[i]["title"])
|
|
||||||
lines = data[i]["content"].split("\n")
|
|
||||||
for j in range(len(lines)):
|
|
||||||
line = lines[j]
|
|
||||||
if filter.lower() in line.lower():
|
|
||||||
index = line.lower().index(filter.lower())
|
|
||||||
print("\t" + str(j) + ":", line[:100])
|
|
||||||
matching.append(data[i])
|
|
||||||
|
|
||||||
return matching
|
|
||||||
|
|
||||||
def readtextfile(path):
|
|
||||||
with io.open(path, mode = "r", encoding = "utf-8") as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
def writetextfile(path, content):
|
|
||||||
with io.open(path, mode = "w", encoding = "utf-8") as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
def editnote(note):
|
|
||||||
content = note["content"]
|
|
||||||
|
|
||||||
backupfilepath = "session/" + filename(note) + str(time.time())
|
|
||||||
writetextfile(backupfilepath, content)
|
|
||||||
writetextfile("session/" + filename(note), content)
|
|
||||||
|
|
||||||
subprocess.call(settings["commands"]["editor"] + ["session/" + filename(note)])
|
|
||||||
newcontent = readtextfile("session/" + filename(note) )
|
|
||||||
|
|
||||||
if newcontent != content:
|
|
||||||
subprocess.call(settings["commands"]["diff"] + [backupfilepath, "session/" + filename(note)])
|
|
||||||
note["content"] = newcontent
|
|
||||||
data.remove(note)
|
|
||||||
data.insert(0, note)
|
|
||||||
savedata()
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("no change")
|
|
||||||
|
|
||||||
def savedata():
|
|
||||||
if settings["mode"] == "remote":
|
|
||||||
writetextfile("session/data.json", json.dumps(data))
|
|
||||||
subprocess.call([settings["commands"]["gpg"], "-q", "--encrypt", "--yes", "--trust-model", "always", "--output", "session/data.acs", "--armor", "-r", settings["gpguser"], "session/data.json"]);
|
|
||||||
newdata = readtextfile("session/data.acs")
|
|
||||||
postdata = "action=push&password=" + settings["password"] + "&data=" + urllib.parse.quote_plus(newdata)
|
|
||||||
writetextfile("session/postdata", postdata)
|
|
||||||
output = subprocess.check_output(["curl", "-X", "POST", "-d", "@session/postdata", settings["url"] + "/handler.php"]).decode("utf-8")
|
|
||||||
print("curl output: " + output)
|
|
||||||
if output != '{"result": "ok"}':
|
|
||||||
if ask("Save failed. Try again?"):
|
|
||||||
savedata()
|
|
||||||
else:
|
|
||||||
writetextfile("session/local.json", json.dumps(data))
|
|
||||||
|
|
||||||
def loaddata():
|
|
||||||
if settings["mode"] == "remote":
|
|
||||||
|
|
||||||
subprocess.call(["curl", "-X", "POST", "-F", "action=fetch", "-F", "password=" + settings["password"], "-o", "session/data.acs", settings["url"] + "/handler.php"])
|
|
||||||
subprocess.call([settings["commands"]["gpg"], "-q", "--yes", "--output", "session/data.json", "--decrypt", "session/data.acs"])
|
|
||||||
|
|
||||||
return json.loads(readtextfile("session/data.json"))
|
|
||||||
|
|
||||||
else:
|
|
||||||
return json.loads(readtextfile("session/local.json"))
|
|
||||||
|
|
||||||
def ask(question):
|
|
||||||
answer = input(question + " [Y/n] ")
|
|
||||||
return answer == "y" or answer == "yes" or answer == ""
|
|
||||||
|
|
||||||
def initdatapath():
|
|
||||||
if not os.path.exists("history"):
|
|
||||||
os.mkdir("history")
|
|
||||||
if os.path.exists("session"):
|
|
||||||
if os.path.exists("session/postdata"):
|
|
||||||
os.remove("session/postdata")
|
|
||||||
shutil.make_archive("history/session" + str(time.time()), "zip", "session")
|
|
||||||
shutil.rmtree("session")
|
|
||||||
os.mkdir("session")
|
|
||||||
|
|
||||||
abspath = os.path.abspath(__file__)
|
|
||||||
dname = os.path.dirname(abspath)
|
|
||||||
os.chdir(dname)
|
|
||||||
|
|
||||||
initdatapath()
|
|
||||||
settings = json.loads(readtextfile("settings.json"))
|
|
||||||
data = loaddata()
|
|
||||||
|
|
||||||
command = ""
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
command = sys.argv[1]
|
|
||||||
if command.startswith("notes://"):
|
|
||||||
command = urllib.parse.unquote(command[8:-1])
|
|
||||||
|
|
||||||
while not (command == "quit" or command == "exit" or command == "q"):
|
|
||||||
|
|
||||||
action = None
|
|
||||||
if command[0:3] == "rm ":
|
|
||||||
action = "delete"
|
|
||||||
command = command[3:]
|
|
||||||
elif command[0:3] == "mv ":
|
|
||||||
action = "rename"
|
|
||||||
command = command[3:]
|
|
||||||
elif command[0:1] == "/":
|
|
||||||
action = "grep"
|
|
||||||
command = command[1:]
|
|
||||||
elif command[0:7] == "export ":
|
|
||||||
action = "export"
|
|
||||||
command = command[7:]
|
|
||||||
elif command == "settings":
|
|
||||||
action = "settings"
|
|
||||||
|
|
||||||
try:
|
|
||||||
index = int(command)
|
|
||||||
note = data[index]
|
|
||||||
except:
|
|
||||||
note = next((note for note in data if note["title"] == command), None)
|
|
||||||
|
|
||||||
if action == "delete":
|
|
||||||
if note and ask("delete '" + note["title"] + "'? "):
|
|
||||||
data.remove(note)
|
|
||||||
savedata()
|
|
||||||
elif action == "rename":
|
|
||||||
if note:
|
|
||||||
newname = input("new name: ")
|
|
||||||
if newname:
|
|
||||||
note["title"] = newname
|
|
||||||
savedata()
|
|
||||||
elif action == "export":
|
|
||||||
if note:
|
|
||||||
writetextfile("session/" + note["title"] + ".md", note["content"])
|
|
||||||
elif action == "settings":
|
|
||||||
subprocess.call(settings["commands"]["editor"] + ["settings.json"])
|
|
||||||
settings = json.loads(readtextfile("settings.json"))
|
|
||||||
elif note and not action == "grep":
|
|
||||||
editnote(note)
|
|
||||||
else:
|
|
||||||
matching = listnotes(command, action == "grep")
|
|
||||||
if len(matching) == 0 and not action == "grep":
|
|
||||||
if ask("create '" + command + "'? "):
|
|
||||||
note = {
|
|
||||||
"title": command,
|
|
||||||
"content": "---\ntitle: " + command + "\ndate: " + time.strftime("%Y-%m-%d") + "\ntags: \n---\n\n"
|
|
||||||
}
|
|
||||||
data.insert(0, note)
|
|
||||||
editnote(note)
|
|
||||||
elif len(matching) == 1:
|
|
||||||
note = matching.pop()
|
|
||||||
if ask("open '" + note["title"] + "'?"):
|
|
||||||
editnote(note)
|
|
||||||
|
|
||||||
command = input("> ")
|
|
|
@ -1,12 +0,0 @@
|
||||||
{
|
|
||||||
"url": "http://localhost:8000",
|
|
||||||
"password": "",
|
|
||||||
"gpguser": "",
|
|
||||||
"commands":
|
|
||||||
{
|
|
||||||
"editor": ["vim"],
|
|
||||||
"gpg": "gpg",
|
|
||||||
"diff": ["diff", "--color"]
|
|
||||||
},
|
|
||||||
"mode": "remote"
|
|
||||||
}
|
|
41
handler.php
41
handler.php
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
require 'settings.php';
|
|
||||||
|
|
||||||
// check authent
|
|
||||||
if ($password && (!isset($_POST['password']) || $_POST['password'] != $password))
|
|
||||||
{
|
|
||||||
echo 'error: authent';
|
|
||||||
}
|
|
||||||
else if (isset($_POST['action']))
|
|
||||||
{
|
|
||||||
$action = $_POST['action'];
|
|
||||||
$path = $datadir . $_POST['name'];
|
|
||||||
switch ($action)
|
|
||||||
{
|
|
||||||
case 'fetch':
|
|
||||||
if (file_exists($path))
|
|
||||||
{
|
|
||||||
echo file_get_contents($path);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'push':
|
|
||||||
$result = file_put_contents($path, $_POST['data']);
|
|
||||||
if ($result === false)
|
|
||||||
{
|
|
||||||
echo 'error: could not save ' . $_POST['name'];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
echo 'error: unknown action ' . $action;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
echo 'error: missing action parameter';
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -13,7 +13,6 @@
|
||||||
<body onload="init()" onkeydown="mainkeydownhandler()" onresize="resize()" onstorage="loadstorage()">
|
<body onload="init()" onkeydown="mainkeydownhandler()" onresize="resize()" onstorage="loadstorage()">
|
||||||
<script src="libs/showdown.min.js"></script>
|
<script src="libs/showdown.min.js"></script>
|
||||||
<script src="libs/vis-network.min.js"></script>
|
<script src="libs/vis-network.min.js"></script>
|
||||||
<script src="libs/openpgp.min.js"></script>
|
|
||||||
<script src="libs/jszip.min.js"></script>
|
<script src="libs/jszip.min.js"></script>
|
||||||
<script src="libs/FileSaver.js"></script>
|
<script src="libs/FileSaver.js"></script>
|
||||||
<script src="main.js"></script>
|
<script src="main.js"></script>
|
||||||
|
|
File diff suppressed because one or more lines are too long
28
readme.md
28
readme.md
|
@ -2,41 +2,21 @@
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
Launch index.html from your web server or try https://notes.ouvaton.org.
|
Call index.html or try https://notes.ouvaton.org.
|
||||||
|
|
||||||
Your notes are stored in your browser local storage.
|
Your notes are stored in your browser local storage.
|
||||||
|
|
||||||
|
You can import a folder of markdown files.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
* command palette: ctrl+shift+p
|
* command palette: ctrl+shift+p
|
||||||
* notes list: ctrl+p
|
* notes list: ctrl+p
|
||||||
|
|
||||||
## Sync feature
|
|
||||||
|
|
||||||
To sync your notes in the cloud:
|
|
||||||
|
|
||||||
* put the source files on your php server
|
|
||||||
* browse index.html
|
|
||||||
* paste your public and private PGP keys as a single file (passphrase is not supported)
|
|
||||||
* refresh the page
|
|
||||||
|
|
||||||
Your data is always encrypted before reaching the server.
|
|
||||||
|
|
||||||
To protect your data file access by a password, edit settings.php and change `$password` variable. The password is sent from browser to server through a post http query, encrypted with ssl if enabled. It is stored unencrypted in your browser local storage and in the settings.php file on server side.
|
|
||||||
|
|
||||||
## Cli tool
|
|
||||||
|
|
||||||
```
|
|
||||||
cd cli
|
|
||||||
python3 app.py
|
|
||||||
```
|
|
||||||
|
|
||||||
Requires python3, curl, gnupg and a text editor.
|
|
||||||
|
|
||||||
## Web clipper
|
## Web clipper
|
||||||
|
|
||||||
Add `clipper\clipper.js` as bookmarklet on your web browser bookmark toolbar. Click to add current URL to a note named *todo*.
|
Add `clipper\clipper.js` as bookmarklet on your web browser bookmark toolbar. Click to add current URL to a note named *todo*.
|
||||||
|
|
||||||
## Export your data
|
## Export your data
|
||||||
|
|
||||||
You can download your notes in a single json data file, or as flat markdown files in a zip archive.
|
You can download your notes as flat markdown files in a zip archive.
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
<?php
|
|
||||||
$datadir = '../data/';
|
|
||||||
$password = '';
|
|
||||||
?>
|
|
Loading…
Reference in New Issue