Compare commits

...

15 Commits
v0.1 ... master

Author SHA1 Message Date
Gregory Soutade
b5e01ca31b Update ChangeLog 2016-06-30 20:13:41 +02:00
Gregory Soutade
b983d6d3f1 Add templates management 2016-06-30 20:13:15 +02:00
Gregory Soutade
d3327f13a2 We don't need to encode data in utf8 for Search 2016-06-23 18:02:09 +02:00
Gregory Soutade
d6b2cad5b8 Forgot authenticated parameter when editing user 2016-06-14 11:31:13 +02:00
Gregory Soutade
b4e7d60760 Add public notes access 2016-06-14 11:28:37 +02:00
Gregory Soutade
72c3a97ffa Add visibility management 2016-06-14 11:09:11 +02:00
Gregory Soutade
38be8017a0 Search didnt works 2016-06-14 11:07:33 +02:00
Gregory Soutade
abcde9da12 Make a more generic base template 2016-06-14 09:44:44 +02:00
Gregory Soutade
9a41eb2f49 Remove useless prints 2016-06-13 18:26:38 +02:00
Gregory Soutade
ff010746a3 Bad method called for Search remove note 2016-01-26 19:31:32 +01:00
Gregory Soutade
712ae02a32 Fix search include in models 2016-01-18 11:36:19 +01:00
Gregory Soutade
e7b8640481 First version of search 2016-01-17 18:48:14 +01:00
Gregory Soutade
a5771bf958 Add ChangeLog 2015-11-08 14:55:36 +01:00
Gregory Soutade
21d361c7ac Add autofocus to login page 2015-11-08 14:55:22 +01:00
Gregory Soutade
f48b0516df Edit note with right click
Remove default parameters (thanks Chrome...)
Add number of registered people
2015-10-12 18:05:07 +02:00
18 changed files with 731 additions and 141 deletions

6
ChangeLog Normal file
View File

@ -0,0 +1,6 @@
v0.2 (30/06/2016)
**User**
Add autofocus to login page
Add search
Add templates

View File

@ -21,6 +21,7 @@
from django.forms import ModelForm
from django import forms
from denote.models import *
from datetime import datetime
class NoteForm(ModelForm):
text = forms.CharField(widget=forms.Textarea(attrs={'rows':'20', 'cols':'150'}))
@ -28,10 +29,20 @@ class NoteForm(ModelForm):
class Meta:
model = Note
exclude = ('author', 'transformed_text', 'long_summary', 'short_summary', 'created_date', 'modified_date', 'category', 'visibility')
exclude = ('author', 'transformed_text', 'long_summary', 'short_summary', 'created_date', 'modified_date', 'category')
class TemplateForm(ModelForm):
text = forms.CharField(widget=forms.Textarea(attrs={'rows':'20', 'cols':'150'}), required=False)
title = forms.CharField(widget=forms.Textarea(attrs={'rows':'1', 'cols':'100'}), required=False)
class Meta:
model = Template
exclude = ('author', 'category')
class UserForm(ModelForm):
password = forms.CharField(required=False)
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.fields['first_name'].label = 'Name'
@ -39,4 +50,4 @@ class UserForm(ModelForm):
class Meta:
model = User
fields = ('first_name', 'username', 'password')
fields = ('first_name', 'username', 'password', 'home_notes_visibility')

View File

@ -24,11 +24,17 @@ import re
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.http import HttpResponse
from django.db.models.signals import pre_init, post_init, pre_delete, post_delete
from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver
import markdown2
from search import Search
class User(AbstractUser):
hidden_categories = models.TextField(blank=True)
home_notes_visibility = models.IntegerField(default=0, choices=[(0, 'private'), (1, 'registered'), (2, 'public')])
default_template = models.ForeignKey('Template', null=True, on_delete=models.SET_NULL)
def getPreference(self, name):
if name == 'hidden_categories':
@ -54,6 +60,16 @@ class Category(models.Model):
name = models.CharField(max_length=50, unique=True, blank=False)
class Note(models.Model):
PRIVATE = 0
REGISTERED = 1
PUBLIC = 2
VISIBILITY = ((PRIVATE, 'Private'),
(REGISTERED, 'Registered'),
(PUBLIC, 'Public')
)
author = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank=False)
long_summary = models.CharField(max_length=255)
@ -62,7 +78,7 @@ class Note(models.Model):
transformed_text = models.TextField()
created_date = models.DateTimeField()
modified_date = models.DateTimeField()
visibility = models.IntegerField(default=0)
visibility = models.IntegerField(default=PRIVATE, choices=VISIBILITY)
category = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL)
def _wrap(self, text, limit, max_limit):
@ -113,8 +129,32 @@ class Note(models.Model):
self.modified_date = datetime.now()
self.transformed_text = markdown2.markdown(self.text, extras=['fenced-code-blocks'])
self._summarize()
s = Search()
super(Note, self).save()
class Template(models.Model):
name = models.CharField(max_length=30, blank=False, unique=True)
author = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank=True)
text = models.TextField(blank=True)
visibility = models.IntegerField(default=Note.PRIVATE, choices=Note.VISIBILITY)
category = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL)
@receiver(post_save, sender=Note)
def post_save_note_signal(sender, **kwargs):
s = Search()
if kwargs['created']:
s.index_note(kwargs['instance'].id)
else:
s.edit_note(kwargs['instance'].id)
@receiver(pre_delete, sender=Note)
def pre_delete_note_signal(sender, **kwargs):
s = Search()
s.delete_note(kwargs['instance'].id)
def manage_category(user, cat_name):
category = None
if cat_name:

196
denote/search.py Executable file
View File

@ -0,0 +1,196 @@
# -*- coding: utf-8 -*-
"""
Copyright 2016 Grégory Soutadé
This file is part of Dénote.
Dynastie 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.
Dynastie 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 Dynastie. If not, see <http://www.gnu.org/licenses/>.
"""
import re
import unicodedata
import os
import operator
import pickle
from django.db import models
import models
#from models import Note
class Search:
MINIMUM_LETTERS = 3
def __init__(self):
self.report = ''
self.tagreg = re.compile('<[^>]+>')
self.htmlreg = re.compile('&[^;]+;')
self.numreg = re.compile('[0-9]+')
self.pat = re.compile(r'\s+')
self.replace_by_space = (u'(', u')', u'#', u'\'', u'{', u'}', u'[', u']',
u'-', u'|', u'\t', u'\\', u'_', u'^' '=', u'+', u'$',
u'£', u'%', u'µ', u'*', u',', u'?', u';', u'.', u'/',
u':', u'!', u'§', u'€', u'²')
# Imported from generator.py
def _addReport(self, string, color=''):
if color != '':
self.report = self.report + '<span style="color:' + color + '">'
self.report = self.report + '<b>' + self.__class__.__name__ + '</b> : '
self.report = self.report + string
if color != '':
self.report = self.report + '</span>'
self.report = self.report + '<br/>\n'
def _addWarning(self, string):
self.addReport(string, 'yellow')
def _addError(self, string):
self.addReport(string, 'red')
def _saveDatabase(self, hashtable):
d = pickle.dumps(hashtable)
f = open(os.environ['DENOTE_ROOT'] + '/_search.db', 'w')
f.write(d)
f.close()
def _loadDatabase(self):
filename = os.environ['DENOTE_ROOT'] + '/_search.db'
if not os.path.exists(filename):
print 'No search index !'
return {}
f = open(filename, 'rb')
hashtable = pickle.load(f)
f.close()
return hashtable
def _strip_accents(self, s):
return ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn'))
def _remove_tag(self, content):
content = self.htmlreg.sub('', content)
content = self.numreg.sub('', content)
content = content.replace('\n', '')
content = content.replace('\r', '')
content = content.replace('"', '')
for c in self.replace_by_space:
content = content.replace(c, ' ')
content = self.tagreg.sub('', content)
content = self.pat.sub(' ', content)
return content
def _prepare_string(self, content):
content = self._remove_tag(content)
content = self._strip_accents(content)
return content
def _indexContent(self, hashtable, index, content, word_weight):
content = self._prepare_string(content)
wordlist = content.split(' ')
for word in wordlist:
if len(word) < self.MINIMUM_LETTERS:
continue
word = word.lower()
if not word in hashtable:
hashtable[word] = []
if not index in hashtable[word]:
hashtable[word].insert(0, [index, word_weight])
else:
weight = hashtable[word][1]
hashtable[word][1] = weight + word_weight
def _index(self, hashtable, index):
note = models.Note.objects.get(pk=index)
self._indexContent(hashtable, index, note.text, 1)
self._indexContent(hashtable, index, note.title, 5)
def _index_note(self, note, saveDatabase=True):
hashtable = self._loadDatabase()
self._index(hashtable, int(note))
if saveDatabase:
self._saveDatabase(hashtable)
def _remove_note(self, note, saveDatabase=True):
hashtable = self._loadDatabase()
if hashtable is None: return
for k, v in hashtable.items():
# For tuples in values
for t in v:
if note == v[0]:
v.remove(t)
if saveDatabase:
self._saveDatabase(hashtable)
def generate_index(self, notes):
hashtable = self._loadDatabase()
for note in notes:
self._indexContent(hashtable, note.id, note.text, 1)
self._indexContent(hashtable, note.id, note.title, 5)
self._saveDatabase(hashtable)
def index_note(self, note):
return self._index_note(note, True)
def delete_note(self, note):
return self._remove_note(note, True)
def edit_note(self, note, saveDatabase=True):
self._remove_note(note, False)
self._index_note(note, True)
def search(self, string):
hashtable = self._loadDatabase()
string = self._prepare_string(string)
wordlist = string.split(' ')
res = {}
for word in wordlist:
if len(word) < Search.MINIMUM_LETTERS:
continue
word = word.lower()
reg = re.compile('.*' + word + '.*')
for key in hashtable.keys():
if reg.match(key):
for note in hashtable[key]:
res[note[0]] = res.get(note[0],0) + note[1]
sorted_res = sorted(res.iteritems(), key=operator.itemgetter(1))
sorted_res.reverse()
res = [sorted_res[i][0] for i in range(len(sorted_res))]
return res

View File

@ -111,7 +111,7 @@ div#categories div.name img
margin : 1em;
}
#main_panel .note .title a
.note .title a
{
color: black;
text-decoration: none;

View File

@ -26,7 +26,7 @@ function getPreference(cname) {
return null;
}
function set_visible(id, visible, display="block")
function set_visible(id, visible, display)
{
widget = document.getElementById(id);
if (visible)
@ -64,21 +64,21 @@ function updateHiddenCategories(cat_id, add)
setPreference('hidden_categories', hidden_categories.join(","));
}
function hide_category(cat_id, update_cookie=true)
function hide_category(cat_id, update_cookie)
{
set_visible("content_" + cat_id, false);
set_visible("minus_" + cat_id, false);
set_visible("content_" + cat_id, false, "");
set_visible("minus_" + cat_id, false, "");
set_visible("plus_" + cat_id, true, "inline-block");
if (update_cookie)
updateHiddenCategories(cat_id, true);
}
function show_category(cat_id, update_cookie=true)
function show_category(cat_id, update_cookie)
{
set_visible("content_" + cat_id, true);
set_visible("content_" + cat_id, true, "block");
set_visible("minus_" + cat_id, true, "inline-block");
set_visible("plus_" + cat_id, false);
set_visible("plus_" + cat_id, false, "");
if (update_cookie)
updateHiddenCategories(cat_id, false);
@ -96,6 +96,7 @@ function category_setup()
category = categories.childNodes[i];
if (category.nodeType != Node.ELEMENT_NODE) continue;
categoryId = category.getAttribute("category_id");
if (categoryId == null) continue;
hide = false;
for(a=0; a<hidden_categories.length;a++)
{
@ -115,12 +116,14 @@ function category_setup()
function startup()
{
category_setup();
if (location.search.search('edit_note') != -1)
edit_note();
}
function edit_category(cat_id, name)
{
set_visible("category_" + cat_id, false);
set_visible("edit_category_" + cat_id, true);
set_visible("category_" + cat_id, false, "");
set_visible("edit_category_" + cat_id, true, "block");
input = document.getElementById("cat_name_" + cat_id);
input.value = name;
@ -129,8 +132,8 @@ function edit_category(cat_id, name)
function end_edit_category(cat_id)
{
set_visible("category_" + cat_id, true);
set_visible("edit_category_" + cat_id, false);
set_visible("category_" + cat_id, true, "block");
set_visible("edit_category_" + cat_id, false, "");
}
function submit_category_name(cat_id, orig)
@ -157,19 +160,24 @@ function handleKeyPress(e, cat_id, orig){
function edit_note()
{
document.body.scrollTop = document.documentElement.scrollTop = 0;
set_visible("title", false);
set_visible("transformed_content", false);
set_visible("edit_button", false);
set_visible("form_delete", false);
set_visible("div_edit", true);
set_visible("title", false, "");
set_visible("transformed_content", false, "");
set_visible("edit_button", false, "");
set_visible("form_delete", false, "");
set_visible("div_edit", true, "block");
}
function cancel_edit_note()
{
document.body.scrollTop = document.documentElement.scrollTop = 0;
set_visible("title", true);
set_visible("transformed_content", true);
set_visible("title", true, "block");
set_visible("transformed_content", true, "block");
set_visible("edit_button", true, "inline");
set_visible("form_delete", true, "inline");
set_visible("div_edit", false);
set_visible("div_edit", false, "");
}
function DoEdit(url) {
window.location.href = '/note/' + url + '?edit_note=1';
return false;
}

View File

@ -2,16 +2,46 @@
<head>
<title>Dénote{% if user.get_full_name|length != 0 %} - {{ user.get_full_name }}{% endif %}</title>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}images/favicon.png" />
<script type="text/javascript">
var hidden_categories = null;
function get_csrf_token() { return '{{ csrf_token }}';}
</script>
{% block head %} {% endblock %}
<link href="{{ STATIC_URL }}css/denote.css" rel="stylesheet" type="text/css"/>
<link href="{{ STATIC_URL }}css/pygments.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="{{ STATIC_URL }}js/denote.js"> </script>
</head>
<body>
<body onload="startup();">
<!-- Header -->
<div class="settings"><a href="/user/edit">Settings</a> <a href="/disconnect">Disconnect</a></div>
<div class="settings"><a href="/public_notes">Public notes</a> {% if authenticated %}<a href="/templates">Templates</a> <a href="/user/edit">Settings</a> <a href="/disconnect">Disconnect</a><br/>
{% endif %}
<form action="/search" method="post">{% csrf_token %}<input name="text"/><input type="submit" value="Search"/></form></div>
<!-- Left panel -->
<div id="left_panel">
<a id="home_icon" href="/" alt="Home"><img src="{{ STATIC_URL }}images/home.png"/></a><br/><br/>
{% if authenticated %}
<form id="form_note_add" action="/note/add" method="POST">
{% csrf_token %}
<a href="#" onclick="document.getElementById('form_note_add').submit();">Add a note</a>
{% if templates_by_name|length != 0 %}
<br/>
<select name="template">
<option value="-1">None</option>
{% for template in templates_by_name %}
{% if template.id == user.default_template.id %}
<option value="{{ template.id }}" selected="1">{{ template.name }}</option>
{% else %}
<option value="{{ template.id }}">{{ template.name }}</option>
{% endif %}
{% endfor %}
</select>
<br/>
{% else %}
<input type="hidden" name="template" value="-1"/>
{% endif %}
</form>
{% endif %}
{% block left %} {% endblock %}
</div>
<div id="main_panel">
<div id="content">
@ -19,6 +49,6 @@
</div>
</div>
<br/><br/><br/>
<center><a href="http://indefero.soutade.fr/p/denote">Dénote</a> 0.1</center>
<center><a href="http://indefero.soutade.fr/p/denote">Dénote</a> 0.2</center>
</body>
</html>

View File

@ -1,29 +1,16 @@
<html>
<head>
<title>Dénote{% if user.get_full_name|length != 0 %} - {{ user.get_full_name }}{% endif %}</title>
<link rel="icon" type="image/png" href="{{ STATIC_URL }}images/favicon.png" />
{% block head %} {% endblock %}
<link href="{{ STATIC_URL }}css/denote.css" rel="stylesheet" type="text/css"/>
<link href="{{ STATIC_URL }}css/pygments.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="{{ STATIC_URL }}js/denote.js"> </script>
{% extends "base.html" %}
{% block head %}
<script type="text/javascript">
var hidden_categories = "{{ user.hidden_categories }}";
hidden_categories = "{{ user.hidden_categories }}";
hidden_categories = hidden_categories.split(",");
function get_csrf_token() { return '{{ csrf_token }}';}
</script>
</head>
<body onload="startup();">
<!-- Header -->
<div class="settings"><a href="/user/edit">Settings</a> <a href="/disconnect">Disconnect</a></div>
<!-- Left panel -->
<div id="left_panel">
<a id="home_icon" href="/" alt="Home"><img src="{{ STATIC_URL }}images/home.png"/></a><br/><br/>
<a href="/note/add">Add a note</a>
{% endblock %}
{% block left %}
<div id="categories">
{% for meta_note in notes_by_category %}
<div class="category" category_id="{{ meta_note.category_id }}">
<div id="category_{{ meta_note.category_id }}" class="name" ondblclick="edit_category({{ meta_note.category_id }}, '{{ meta_note.category }}')">
<img id="minus_{{ meta_note.category_id }}" src="{{ STATIC_URL }}images/denote_minus.png" onclick="hide_category({{ meta_note.category_id }});"/><img id="plus_{{ meta_note.category_id }}" src="{{ STATIC_URL }}images/denote_plus.png" onclick="show_category({{ meta_note.category_id }});"/>{{ meta_note.category }} ({{ meta_note.notes|length }})
<img id="minus_{{ meta_note.category_id }}" src="{{ STATIC_URL }}images/denote_minus.png" onclick="hide_category({{ meta_note.category_id }},true);"/><img id="plus_{{ meta_note.category_id }}" src="{{ STATIC_URL }}images/denote_plus.png" onclick="show_category({{ meta_note.category_id }},true);"/>{{ meta_note.category }} ({{ meta_note.notes|length }})
</div>
<div class="edit_category" id="edit_category_{{ meta_note.category_id }}">
<form id="form_edit_category_{{ meta_note.category_id }}" action="/category/edit/{{ meta_note.category_id }}" method="post" onsubmit="return submit_category_name({{ meta_note.category_id }}, '{{ meta_note.category }}');">
@ -34,7 +21,7 @@
<div id="content_{{ meta_note.category_id }}" class="content" >
{% for note in meta_note.notes %}
<div class="note">
<a href="/note/{{ note.id}}"><div class="title">{{ note.title }}</div></a>
<a href="/note/{{ note.id}}" oncontextmenu="return DoEdit('{{ note.id}}');"><div class="title">{{ note.title }}</div></a>
<div class="date">{{ note.created_date }}</div>
<div class="summary">{{ note.short_summary }}</div>
</div>
@ -45,31 +32,17 @@
{% if notes_without_category|length != 0 %}
<div class="category" category_id="-1">
<div id="category_-1" class="name">
<img id="minus_-1" src="{{ STATIC_URL }}images/denote_minus.png" onclick="hide_category(-1);"/><img id="plus_-1" src="{{ STATIC_URL }}images/denote_plus.png" onclick="show_category(-1);"/>Other ({{ notes_without_category|length }})</div>
<img id="minus_-1" src="{{ STATIC_URL }}images/denote_minus.png" onclick="hide_category(-1,true);"/><img id="plus_-1" src="{{ STATIC_URL }}images/denote_plus.png" onclick="show_category(-1,true);"/>Other ({{ notes_without_category|length }})</div>
<div id="content_-1" class="content">
{% for note in notes_without_category %}
<div class="note">
<a href="/note/{{ note.id}}"><div class="title">{{ note.title }}</div></a>
<a href="/note/{{ note.id}}" oncontextmenu="return DoEdit('{{ note.id}}');"><div class="title">{{ note.title }}</div></a>
<div class="date">{{ note.created_date }}</div>
<div class="summary">{{ note.short_summary }}</div>
</div>
{% endfor %}
</div>
</div>
{% else %}
{% if notes_by_category|length == 0 %}
<b>Any note</b>
{% endif %}
{% endif %}
</div>
</div>
<!-- Main panel -->
<div id="main_panel">
<div id="content">
{% block content %} {% endblock %}
</div>
</div>
<br/><br/><br/>
<center><a href="http://indefero.soutade.fr/p/denote">Dénote</a> 0.1</center>
</body>
</html>
{% endblock %}

View File

@ -8,6 +8,20 @@
<form action="/user/edit" method="post">
{% csrf_token %}
{{ form.as_p }}
<p>
<label for="id_default_template">Default template:</label>
<select id="id_default_template" name="default_template">
<option value="-1">None</option>
{% for template in templates_by_name %}
{% if template.id == user.default_template.id %}
<option value="{{ template.id }}" selected="1">{{ template.name }}</option>
{% else %}
<option value="{{ template.id }}">{{ template.name }}</option>
{% endif %}
{% endfor %}
</select>
</p>
<input type="submit" name="edit" value="Edit" /><input type="submit" name="cancel" value="Cancel" /><input type="submit" name="delete" value="Delete" onclick="return confirm('Do you really want to delete your account ?')"/>
</form>
{% endblock %}

View File

@ -23,10 +23,12 @@
{% csrf_token %}
{% if login_failed %} <p id="login_failed">Login or password is invalid</p> {% endif %}
<table>
<tr><td>Login</td><td><input type="text" name="login"/></td></tr>
<tr><td>Login</td><td><input type="text" name="login" autofocus/></td></tr>
<tr><td>Password</td><td><input id="password" type="password" name="password"/></td></tr>
<tr><td/><td><input type="submit" value="Connect"/></td></tr>
<tr><td/><td><a href="/user/add">Create an account</a></td></tr>
<tr><td/><td><a href="/user/add">Create an account</a><br/>({{ nb_people_registered }} people(s) registered)</td></tr>
<tr><td></td><td></td></tr>
<tr><td></td><td><a href="/public_notes">Public notes</a></td></tr>
</table>
</form>
</div>

View File

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block left %}
{% for note in public_notes %}
<div id="categories">
<div class="note">
<a href="/note/{{ note.author.id}}/{{ note.id }}"><div class="title">{{ note.title }}</div></a>
<div class="date">{{ note.created_date }}</div>
<div class="summary">{{ note.short_summary }}</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block content %}
<div class="note">
<div id="title" class="title">{{ note.title }}</div>
<div id="transformed_content">{{ note.transformed_text|safe }}</div>
</div>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "base.html" %}
{% block left %}
{% for note in public_notes %}
<div id="categories">
<div class="note">
<a href="/note/{{ note.author.id}}/{{ note.id }}"><div class="title">{{ note.title }}</div></a>
<div class="date">{{ note.created_date }}</div>
<div class="summary">{{ note.short_summary }}</div>
</div>
</div>
{% endfor %}
{% endblock %}
{% block content %}
{% for note in notes %}
<div class="note">
<div class="title"><a href="/note/{{ note.author.id }}/{{ note.id }}">{{ note.title }}</a></div>
<div class="date">{{ note.modified_date }}</div>
<div class="summary">{{ note.long_summary }}</div>
</div>
{% endfor %}
{% if notes|length == 0 %}
<b>Any note</b>
{% endif %}
{% endblock %}

View File

@ -4,12 +4,15 @@
{% for note in notes %}
<div class="note">
{% if note.category != None %}
<div class="title"><a href="/note/{{ note.id }}">{{ note.title }} [{{ note.category.name }}]</a></div>
<div class="title"><a href="/note/{{ note.id}}" oncontextmenu="return DoEdit('{{ note.id}}');">{{ note.title }} [{{ note.category.name }}]</a></div>
{% else %}
<div class="title"><a href="/note/{{ note.id }}">{{ note.title }}</a></div>
<div class="title"><a href="/note/{{ note.id}}" oncontextmenu="return DoEdit('{{ note.id}}');">{{ note.title }}</a></div>
{% endif %}
<div class="date">{{ note.modified_date }}</div>
<div class="summary">{{ note.long_summary }}</div>
</div>
{% endfor %}
{% if notes|length == 0 %}
<b>Any note</b>
{% endif %}
{% endblock %}

View File

@ -2,12 +2,16 @@
{% block content %}
<div class="note">
{% if note != None %}
{% if note and note_form != None %}
<div id="title" class="title" ondblclick="edit_note();">{{ note.title }}</div>
{% if note.category != None %}
{% else %}
<div id="title" class="title">{{ note.title }}</div>
{% endif %}
{% if note and note.category != None %}
<div class="category">{{ note.category.name }}</div>
{% endif %}
<div id="transformed_content">{{ note.transformed_text|safe }}</div>
{% if note and note_form != None %}
<div class="edit_button"><input id="edit_button" type="button" value="Edit" onclick="edit_note();"/> <form id="form_delete" action="/note/{{ note.id }}" method="post">{% csrf_token %}<input type="submit" name="delete" value="Delete" onclick="return confirm('Do you really want to delete this note ?')"/></form></div>
<div id="div_edit">
<form id="form_edit" action="/note/{{ note.id }}" method="post">{% csrf_token %}
@ -21,58 +25,11 @@
<input type="submit" name="edit" value="Edit" />
<input type="button" value="Cancel" onclick="cancel_edit_note();"/>
</form>
<b>Markdown syntax</b><br /><br />
<table>
<tr>
<td class="markdown_help">
<pre style="display:inline">_italic_</pre> <span style="font-style:italic">italic</span><br/>
<pre style="display:inline">**bold**</pre> <span style="font-weight:bold">bold</span><br/>
<pre style="display:inline">~~line through~~</pre> <span style="text-decoration:line-through">line through</span><br/>
<pre style="display:inline">~underline~</pre> <span style="text-decoration:underline">underline</span><br/>
<pre style="display:inline">>Citation</pre><br/>
<pre>
* Unordered list
* Second element
</pre>
<ul>
<li>Unordered list
<li>Second element
</ul>
<pre>
1. Ordered list
1. Second element
</pre>
<ol>
<li>Ordered list
<li>Second element
</ol>
<pre style="display:inline">![Picture](https://bits.wikimedia.org/images/wikimedia-button.png)</pre><img src="https://bits.wikimedia.org/images/wikimedia-button.png" alt="Picture"/><br/>
<pre style="display:inline">#[Inline Picture](https://bits.wikimedia.org/images/wikimedia-button.png)</pre><img src="https://bits.wikimedia.org/images/wikimedia-button.png" alt="Picture"/><br/>
<pre style="display:inline">[Link](http://www.wikipedia.org)</pre> <a href="http://www.wikipedia.org">Link</a><br/><br/>
<pre>
Code : 4 whitespaces ahead
</pre>
</td>
<td>
<pre># Title # or
Title
=====</pre>
<h1>Title</h1>
<pre>## Sub title ## or
Sub title
---------</pre>
<h2>Sub title</h2>
<pre>### Sub sub title ###</pre>
<h3>Sub sub title</h3>
</td>
</tr>
</table>
</div>
{% else %}
{% else %}
<div class="form_add">
<form action="/note/add" method="post">{% csrf_token %}
{{ note_form.as_p }}
Category <input name="category" list=all_categories>
Category <input name="category" list=all_categories {% if category != None %} value="{{ category }}" {% endif %}>
<datalist id="all_categories">
{% for category in categories %}
<option value="{{ category.name }}"></option>
@ -81,7 +38,7 @@
<input type="submit" name="add" value="Add" />
<input type="submit" name="cancel" value="Cancel" />
</form>
</div>
{% endif %}
<b>Markdown syntax</b><br /><br />
<table>
<tr>
@ -131,6 +88,6 @@
</td>
</tr>
</table>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,77 @@
{% extends "base.html" %}
{% block content %}
<div class="template">
{% if template != None %}
<form action="/template/{{ template.id }}" method="post">{% csrf_token %}
{% else %}
<form action="/template/add" method="post">{% csrf_token %}
{% endif %}
{{ template_form.as_p }}
Category <input name="category" list="all_categories" {% if template.category != None %} value="{{ template.category.name }}" {% endif %}>
<datalist id="all_categories">
{% for category in categories %}
<option value="{{ category.name }}"></option>
{% endfor %}
</datalist><br/>
{% if template != None %}
<input type="submit" name="edit" value="Edit" />
<input type="submit" name="delete" value="Delete" onclick="return confirm('Do you really want to delete this template ?')"/>
{% else %}
<input type="submit" name="add" value="Add"/>
{% endif %}
<input type="submit" name="cancel" value="Cancel"/>
</form>
<br/>
<b>Markdown syntax</b><br /><br />
<table>
<tr>
<td class="markdown_help">
<pre style="display:inline">_italic_</pre> <span style="font-style:italic">italic</span><br/>
<pre style="display:inline">**bold**</pre> <span style="font-weight:bold">bold</span><br/>
<pre style="display:inline">~~line through~~</pre> <span style="text-decoration:line-through">line through</span><br/>
<pre style="display:inline">~underline~</pre> <span style="text-decoration:underline">underline</span><br/>
<pre style="display:inline">>Citation</pre><br/>
<pre>
* Unordered list
* Second element
</pre>
<ul>
<li>Unordered list
<li>Second element
</ul>
<pre>
1. Ordered list
1. Second element
</pre>
<ol>
<li>Ordered list
<li>Second element
</ol>
<pre style="display:inline">![Picture](https://bits.wikimedia.org/images/wikimedia-button.png)</pre><img src="https://bits.wikimedia.org/images/wikimedia-button.png" alt="Picture"/><br/>
<pre style="display:inline">#[Inline Picture](https://bits.wikimedia.org/images/wikimedia-button.png)</pre><img src="https://bits.wikimedia.org/images/wikimedia-button.png" alt="Picture"/><br/>
<pre style="display:inline">[Link](http://www.wikipedia.org)</pre> <a href="http://www.wikipedia.org">Link</a><br/><br/>
<pre>
Code : 4 whitespaces ahead OR
```language
Code
```
</pre>
</td>
<td>
<pre># Title # or
Title
=====</pre>
<h1>Title</h1>
<pre>## Sub title ## or
Sub title
---------</pre>
<h2>Sub title</h2>
<pre>### Sub sub title ###</pre>
<h3>Sub sub title</h3>
</td>
</tr>
</table>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% block content %}
{% for template in templates %}
<div class="template">
<div class="title"><a href="/template/{{ template.id}}" oncontextmenu="return DoEdit('{{ template.id }}');">{{ template.name }}</a></div>
</div>
{% endfor %}
{% if templates|length == 0 %}
<b>Any template</b>
{% endif %}
<div class="settings"><a href="/template/add">Add a Template</a><br/></div>
{% endblock %}

View File

@ -24,10 +24,18 @@ urlpatterns = patterns('',
url(r'^index[/]?$', 'denote.views.index', name='index'),
url(r'^[/]?$', 'denote.views.index', name='index'),
url(r'^disconnect?$', 'denote.views.disconnect', name='disconnect'),
url(r'^user/add$','denote.views.new_user', name='add_user'),
url(r'^user/add$','denote.views.new_user', name='new_user'),
url(r'^user/edit$','denote.views.edit_user', name='edit_user'),
url(r'^note/add$', 'denote.views.add_note', name='add_note'),
url(r'^note/(\d+)$', 'denote.views.note', name='note'),
url(r'^note/(\d+)/(\d+)$', 'denote.views.public_note', name='public_note'),
url(r'^public_notes$', 'denote.views.public_notes', name='public_notes'),
url(r'^category/edit/(\d)$','denote.views.edit_category', name='edit_category'),
url(r'^preferences$', 'denote.views.preferences', name='preferences'),
url(r'^search$', 'denote.views.search', name='search'),
url(r'^generate_search_index$', 'denote.views.generate_search_index', name='generate_search_index'),
url(r'^template/add$','denote.views.add_template', name='add_template'),
url(r'^template/(\d+)$','denote.views.template', name='template'),
url(r'^templates$','denote.views.templates', name='templates'),
)

View File

@ -18,15 +18,17 @@
along with Dénote. If not, see <http://www.gnu.org/licenses/>.
"""
import os
from datetime import datetime
from django.http import HttpResponseRedirect, HttpResponse, Http404
from django.http import HttpResponseRedirect, HttpResponse, Http404, HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from django.contrib.auth import authenticate, login, logout
from django.shortcuts import render
from denote.models import *
from denote.forms import *
from denote.search import *
def index(request):
if request.user.is_authenticated():
@ -46,7 +48,10 @@ def index(request):
else:
return user_home(request, request.user)
c = {'login_failed' : login_failed}
c = {
'login_failed' : login_failed,
'nb_people_registered' : User.objects.all().count()
}
return render(request, 'login.html', c)
@ -79,7 +84,7 @@ def new_user(request):
form = UserForm()
c = {'login' : login_val, 'password' : password, 'form': form}
return render(request, 'add_user.html', c)
@login_required
@ -89,13 +94,22 @@ def edit_user(request):
if request.method == 'POST':
if 'edit' in request.POST:
template_id = request.POST['default_template']
if template_id == '-1':
template = None
else:
try:
template= Template.objects.get(author=user.id, id=template_id)
except:
template = None
user.default_template = template
form = UserForm(request.POST, instance=user, initial={'password':''})
if form.is_valid():
form.save()
if request.POST['password'] != '':
user.set_password(request.POST['password'])
user.save()
edited = True
edited = True
else:
if 'delete' in request.POST:
logout(request)
@ -105,11 +119,45 @@ def edit_user(request):
login = 'login' in request.POST and request.POST['login'] or ''
form = UserForm(instance=user, initial={'password':'', 'login':login})
c = {'user_to_edit' : user, 'form' : form, 'edited' : edited}
templates = Template.objects.filter(author=user.id).order_by('name')
templates_by_name = []
for template in templates:
t = {}
t['name'] = template.name
t['id'] = template.id
templates_by_name.append(t)
c = {'authenticated' : True, 'user_to_edit' : user, 'form' : form, 'edited' : edited}
c['templates_by_name'] = templates_by_name
return render(request, 'edit_user.html', c)
def _prepare_template_context(user):
categories = Category.objects.filter(author=user.id).order_by('name')
templates = Template.objects.filter(author=user.id).order_by('name')
templates_by_name = []
for template in templates:
t = {}
t['name'] = template.name
t['id'] = template.id
templates_by_name.append(t)
context = {
'user': user,
'authenticated' : True,
'categories': categories,
'templates_by_name': templates_by_name,
}
return context
def _prepare_note_context(user):
if not user.is_authenticated():
return {
'authenticated' : False,
}
categories = Category.objects.filter(author=user.id).order_by('name')
notes_by_category = []
need_refresh = False
@ -127,11 +175,21 @@ def _prepare_note_context(user):
categories = Category.objects.filter(author=user.id).order_by('name')
notes_without_category = Note.objects.filter(author=user,category=None).order_by('-modified_date')
templates = Template.objects.filter(author=user.id).order_by('name')
templates_by_name = []
for template in templates:
t = {}
t['name'] = template.name
t['id'] = template.id
templates_by_name.append(t)
context = {
'user': user,
'authenticated' : True,
'notes_by_category': notes_by_category,
'categories': categories,
'notes_without_category': notes_without_category,
'templates_by_name': templates_by_name,
}
return context
@ -150,23 +208,32 @@ def user_home(request, user):
def add_note(request):
user = request.user
if request.method == 'POST':
if 'add' in request.POST:
note = Note(author=user, created_date=datetime.now())
note.category = manage_category(user, request.POST['category'])
form = NoteForm(request.POST, instance=note)
if form.is_valid():
form.save()
return HttpResponseRedirect('/note/%d' % (note.id))
else:
if 'cancel' in request.POST:
return HttpResponseRedirect('/')
if 'add' in request.POST:
note = Note(author=user, created_date=datetime.now())
note.category = manage_category(user, request.POST['category'])
form = NoteForm(request.POST, instance=note)
if form.is_valid():
form.save()
return HttpResponseRedirect('/note/%d' % (note.id))
elif 'cancel' in request.POST:
return HttpResponseRedirect('/')
else:
form = NoteForm()
note = None
template_id = request.POST['template']
if template_id != '-1':
note = Template.objects.get(id=template_id, author=user.id)
if not note:
note = Note(visibility=user.home_notes_visibility)
form = NoteForm(instance=note)
context = _prepare_note_context(user)
context['note_form'] = form
context['note'] = None
if note.category:
context['category'] = note.category.name
return render(request, 'user_note.html', context)
@login_required
@ -196,6 +263,50 @@ def note(request, note_id):
return render(request, 'user_note.html', context)
def public_note(request, user_id, note_id):
user = request.user
try:
note = Note.objects.get(pk=note_id, author=user_id)
except:
raise Http404
if note is None:
raise Http404
if not user or not user.is_authenticated():
if note.visibility != Note.PUBLIC:
return HttpResponseForbidden()
else:
if note.visibility == Note.PRIVATE and\
user_id != user.id:
return HttpResponseForbidden()
if user.is_authenticated():
public_notes = Note.objects.filter(author=user_id, visibility__gte=Note.REGISTERED).order_by('-modified_date')
else:
public_notes = Note.objects.filter(author=user_id, visibility__gte=Note.PUBLIC).order_by('-modified_date')
context = _prepare_note_context(user)
context['note'] = note
context['public_notes'] = public_notes
return render(request, 'public_note.html', context)
def public_notes(request):
user = request.user
if user.is_authenticated():
public_notes = Note.objects.filter(visibility__gte=Note.REGISTERED).order_by('-modified_date')
else:
public_notes = Note.objects.filter(visibility__gte=Note.PUBLIC).order_by('-modified_date')
context = _prepare_note_context(user)
context['notes'] = public_notes[:50]
context['public_notes'] = public_notes[:50]
return render(request, 'public_notes.html', context)
@login_required
def edit_category(request, category_id):
user = request.user
@ -221,12 +332,9 @@ def edit_category(request, category_id):
@login_required
def preferences(request):
print request.method
if request.method != 'POST':
raise Http404
print request.POST.items()
if 'get' in request.POST and 'name' in request.POST:
return request.user.getPreference(request.POST['name'])
elif 'set' in request.POST and 'name' in request.POST and \
@ -235,3 +343,100 @@ def preferences(request):
else:
raise Http404
def search(request):
context = _prepare_note_context(request.user)
ref = request.META['HTTP_REFERER']
if 'text' in request.POST:
text = request.POST['text']
else:
return HttpResponseRedirect(ref)
s = Search()
note_list = s.search(text)
if request.user.is_authenticated():
notes = Note.objects.filter(pk__in=note_list, author=request.user)
context['notes'] = notes
context['note_form'] = NoteForm()
return render(request, 'user_index.html', context)
else:
notes = Note.objects.filter(pk__in=note_list, visibility__gte=Note.PUBLIC)
context['notes'] = notes
return render(request, 'public_notes.html', context)
@login_required
def generate_search_index(request):
if os.path.exists('_search.db'):
os.path.remove('_search.db')
s = Search()
s.generate_index(Note.objects.all())
return HttpResponseRedirect('/')
@login_required
def add_template(request):
user = request.user
if request.method == 'POST':
if 'add' in request.POST:
template = Template(author=user)
template.category = manage_category(user, request.POST['category'])
form = TemplateForm(request.POST, instance=template)
if form.is_valid():
form.save()
return HttpResponseRedirect('/templates')
else:
if 'cancel' in request.POST:
return HttpResponseRedirect('/templates')
else:
template = Template(visibility=user.home_notes_visibility)
form = TemplateForm(instance=template)
context = _prepare_template_context(user)
context['template_form'] = form