Page data moved to db. Prep for access control
This commit is contained in:
parent
292631ed21
commit
a528ac19cb
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
# piki
|
||||
data/media
|
||||
data/pages
|
||||
data/mycreole
|
||||
data/static
|
||||
data/whoosh
|
||||
db.sqlite3
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"creation_time": 1728465254,
|
||||
"modified_time": 1728465254,
|
||||
"modified_user": "system-page"
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
= Index
|
||||
<<allpages>>
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"creation_time": 1728465495,
|
||||
"modified_time": 1728649989,
|
||||
"modified_user": "system-page"
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
= Default startpage
|
||||
Edit this page to get your own first startpage.
|
||||
|
||||
If you need need assistance to edit a page, visit the [[/helpview/main|help pages]].
|
||||
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"modified_time": 1729022206,
|
||||
"modified_user": "system-page",
|
||||
"creation_time": 1729022206
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
= Tree
|
||||
<<allpagestree>>
|
@ -1,16 +1,27 @@
|
||||
def read_page(request, rel_path):
|
||||
return "private" not in rel_path or write_page(request, rel_path)
|
||||
class access_control(object):
|
||||
def __init__(self, request, rel_path):
|
||||
self._request = request
|
||||
self._rel_path = rel_path
|
||||
|
||||
def may_read(self):
|
||||
return "private" not in self._rel_path or self.may_write()
|
||||
|
||||
def write_page(request, rel_path):
|
||||
return request.user.is_authenticated and request.user.username in ['root', 'dirk']
|
||||
def may_write(self):
|
||||
# /!\ rel_path is the filsystem rel_path - caused by the flat folder structure /!\
|
||||
return self._request.user.is_authenticated and self._request.user.username in ['root', 'dirk']
|
||||
|
||||
def may_read_attachment(self):
|
||||
return self.may_read()
|
||||
|
||||
def may_modify_attachment(self):
|
||||
return self.may_write()
|
||||
|
||||
|
||||
def read_attachment(request, rel_path):
|
||||
# /!\ rel_path is the filsystem rel_path - caused by the flat folder structure /!\
|
||||
return True
|
||||
# Interface for external module mycreole
|
||||
return access_control(request, rel_path).may_read_attachment()
|
||||
|
||||
|
||||
def modify_attachment(request, rel_path):
|
||||
# /!\ rel_path is the filsystem rel_path - caused by the flat folder structure /!\
|
||||
return request.user.is_authenticated and request.user.username in ['root', 'dirk']
|
||||
# Interface for external module mycreole
|
||||
return access_control(request, rel_path).may_modify_attachment()
|
||||
|
@ -1,3 +1,17 @@
|
||||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
|
||||
# Register your models here.
|
||||
from .models import PikiPage
|
||||
|
||||
|
||||
class PikiPageAdmin(SimpleHistoryAdmin):
|
||||
list_display = ('rel_path', 'tags', 'deleted')
|
||||
history_list_display = ('rel_path', 'tags', 'deleted')
|
||||
search_fields = ('rel_path', 'tags', )
|
||||
list_filter = (
|
||||
('deleted', admin.BooleanFieldListFilter),
|
||||
)
|
||||
ordering = ["rel_path"]
|
||||
|
||||
|
||||
admin.site.register(PikiPage, PikiPageAdmin)
|
||||
|
@ -5,7 +5,7 @@ import os
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from pages import access
|
||||
from pages.access import access_control
|
||||
import pages.parameter
|
||||
from .help import actionbar as actionbar_add_help
|
||||
import mycreole
|
||||
@ -74,11 +74,12 @@ def actionbar(context, request, caller_name, **kwargs):
|
||||
bar = context[context.ACTIONBAR]
|
||||
if not cms_mode_active(request):
|
||||
if caller_name in ['page', 'edit', 'delete', 'rename']:
|
||||
if access.write_page(request, kwargs["rel_path"]):
|
||||
acc = access_control(request, kwargs["rel_path"])
|
||||
if acc.may_write():
|
||||
add_page_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
|
||||
if access.modify_attachment(request, kwargs["rel_path"]):
|
||||
if acc.may_modify_attachment():
|
||||
add_manageupload_menu(request, bar, kwargs['upload_path'], kwargs.get('is_available', False))
|
||||
if access.read_page(request, kwargs["rel_path"]):
|
||||
if acc.may_read():
|
||||
add_meta_menu(request, bar, kwargs["rel_path"], kwargs.get('is_available', False))
|
||||
elif caller_name == 'helpview':
|
||||
actionbar_add_help(context, request, **kwargs)
|
||||
|
@ -3,17 +3,13 @@ from django import forms
|
||||
from django.forms.renderers import BaseRenderer
|
||||
from django.forms.utils import ErrorList
|
||||
|
||||
from .models import PikiPage
|
||||
|
||||
class EditForm(forms.Form): # Note that it is not inheriting from forms.ModelForm
|
||||
page_txt = forms.CharField(max_length=20000, label="Page source text", widget=forms.Textarea(attrs={"rows": "20"}))
|
||||
page_tags = forms.CharField(max_length=500, label="Tags (words separated by spaces)", required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
page_data = kwargs.pop("page_data")
|
||||
page_tags = kwargs.pop("page_tags")
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['page_txt'].initial = page_data
|
||||
self.fields['page_tags'].initial = page_tags
|
||||
class EditForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = PikiPage
|
||||
fields = ["page_txt", "tags", "owner", "group"]
|
||||
|
||||
|
||||
class RenameForm(forms.Form): # Note that it is not inheriting from forms.ModelForm
|
||||
|
57
pages/management/commands/import_system_pages.py
Normal file
57
pages/management/commands/import_system_pages.py
Normal file
@ -0,0 +1,57 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from pages.models import PikiPage
|
||||
|
||||
from datetime import datetime
|
||||
import fstools
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
SYSTEM_PAGES = {
|
||||
"tree": """= Tree
|
||||
<<allpagestree>>""",
|
||||
"index": """= Index
|
||||
<<allpages>>""",
|
||||
}
|
||||
|
||||
|
||||
def add_page_data(rel_path, tags, page_txt, creation_time, creation_user, modified_time, modified_user):
|
||||
try:
|
||||
page = PikiPage.objects.get(rel_path=rel_path)
|
||||
except PikiPage.DoesNotExist:
|
||||
page = PikiPage(rel_path=rel_path)
|
||||
#
|
||||
page.tags = tags
|
||||
page.page_txt = page_txt
|
||||
#
|
||||
page.creation_time = creation_time
|
||||
try:
|
||||
page.creation_user = User.objects.get(username=creation_user)
|
||||
except User.DoesNotExist:
|
||||
page.creation_user = None
|
||||
page.modified_time = modified_time
|
||||
try:
|
||||
page.modified_user = User.objects.get(username=modified_user)
|
||||
except User.DoesNotExist:
|
||||
page.modified_user = None
|
||||
#
|
||||
page.save()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
for rel_path in SYSTEM_PAGES:
|
||||
self.stdout.write(self.style.MIGRATE_HEADING("Migration of page '%s'" % rel_path))
|
||||
#
|
||||
dtm = datetime.now(ZoneInfo("UTC"))
|
||||
add_page_data(
|
||||
rel_path,
|
||||
"",
|
||||
SYSTEM_PAGES[rel_path],
|
||||
dtm,
|
||||
None,
|
||||
dtm,
|
||||
None
|
||||
)
|
73
pages/management/commands/migrate_to_db.py
Normal file
73
pages/management/commands/migrate_to_db.py
Normal file
@ -0,0 +1,73 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import BaseCommand
|
||||
from pages.page import full_path_all_pages, page_wrapped
|
||||
|
||||
from pages.models import PikiPage
|
||||
|
||||
from datetime import datetime
|
||||
import fstools
|
||||
import os
|
||||
import shutil
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
||||
def add_page_data(rel_path, tags, page_txt, creation_time, creation_user, modified_time, modified_user):
|
||||
try:
|
||||
page = PikiPage.objects.get(rel_path=rel_path)
|
||||
except PikiPage.DoesNotExist:
|
||||
page = PikiPage(rel_path=rel_path)
|
||||
#
|
||||
page.tags = tags
|
||||
page.page_txt = page_txt
|
||||
#
|
||||
page.creation_time = datetime.fromtimestamp(creation_time, ZoneInfo("UTC"))
|
||||
creation_user = creation_user or "dirk"
|
||||
page.creation_user = User.objects.get(username=creation_user)
|
||||
modified_user = modified_user or "dirk"
|
||||
page.modified_time = datetime.fromtimestamp(modified_time, ZoneInfo("UTC"))
|
||||
page.modified_user = User.objects.get(username=modified_user)
|
||||
page.owner = page.owner or page.creation_user
|
||||
#
|
||||
page.save()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
for path in full_path_all_pages():
|
||||
fs_page = page_wrapped(None, path)
|
||||
if fs_page._page.is_available():
|
||||
self.stdout.write(self.style.MIGRATE_HEADING("Migration of page '%s'" % fs_page.rel_path))
|
||||
for history_number in fs_page._page.history_numbers_list():
|
||||
self.stdout.write(self.style.MIGRATE_HEADING(" * Adding history version %d" % history_number))
|
||||
h_page = page_wrapped(None, path, history_version=history_number)
|
||||
add_page_data(
|
||||
rel_path=h_page.rel_path,
|
||||
tags=h_page.tags,
|
||||
page_txt=h_page._page.raw_page_src,
|
||||
#
|
||||
creation_time=h_page.creation_time,
|
||||
creation_user=h_page.creation_user,
|
||||
modified_time=h_page.modified_time,
|
||||
modified_user=h_page.modified_user
|
||||
)
|
||||
#
|
||||
self.stdout.write(self.style.MIGRATE_HEADING(" * Adding current version"))
|
||||
add_page_data(
|
||||
rel_path=fs_page.rel_path,
|
||||
tags=fs_page.tags,
|
||||
page_txt=fs_page._page.raw_page_src,
|
||||
#
|
||||
creation_time=fs_page.creation_time,
|
||||
creation_user=fs_page.creation_user,
|
||||
modified_time=fs_page.modified_time,
|
||||
modified_user=fs_page.modified_user
|
||||
)
|
||||
#
|
||||
src = os.path.join(path, "attachments")
|
||||
if os.path.isdir(src):
|
||||
dst = os.path.join(settings.MYCREOLE_ROOT, fs_page.rel_path)
|
||||
for attachment in fstools.filelist(src):
|
||||
self.stdout.write(self.style.MIGRATE_HEADING(" * Copy attachment ''%s to new location" % os.path.basename(attachment)))
|
||||
fstools.mkdir(dst)
|
||||
shutil.copy(attachment, dst)
|
@ -8,6 +8,11 @@ def permission_denied_msg_page(request, rel_path):
|
||||
messages.error(request, _("Permission denied: You don't have sufficient acces to the Page '%s'. Please contact the adminstrator.") % rel_path)
|
||||
|
||||
|
||||
def deleted_page(request):
|
||||
# TODO: Add translation for this message
|
||||
messages.info(request, _("Page was deleted. Recover not yet implemented. Use the 'Administration' area for recovery. Rebuild the search index afterwards."))
|
||||
|
||||
|
||||
def unavailable_msg_page(request, rel_path):
|
||||
# TODO: Add translation for this message
|
||||
messages.info(request, _("Unavailable: The Page '%s' is not available. Create it or follow a valid link, please.") % rel_path)
|
||||
@ -38,6 +43,11 @@ def page_renamed(request):
|
||||
messages.info(request, _('The page has been renamed.'))
|
||||
|
||||
|
||||
def internal_error(request):
|
||||
# TODO: Add translation for this message
|
||||
messages.error(request, _('internal ERROR: Action not performed..'))
|
||||
|
||||
|
||||
def history_version_display(request, rel_path, history_version):
|
||||
# TODO: Add translation for this message
|
||||
messages.warning(request, _("You see an old version of the page (Version = %d). Click <a href='%s'>here</a> to recover this Version.") % (
|
||||
|
63
pages/migrations/0001_initial.py
Normal file
63
pages/migrations/0001_initial.py
Normal file
@ -0,0 +1,63 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-21 04:20
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalPikiPage',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('rel_path', models.CharField(db_index=True, max_length=1000)),
|
||||
('page_txt', models.TextField(max_length=50000)),
|
||||
('tags', models.CharField(blank=True, max_length=1000, null=True)),
|
||||
('deleted', models.BooleanField(default=False)),
|
||||
('creation_time', models.DateTimeField(blank=True, null=True)),
|
||||
('modified_time', models.DateTimeField(blank=True, null=True)),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('creation_user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('group', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='auth.group')),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('modified_user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('owner', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical piki page',
|
||||
'verbose_name_plural': 'historical piki pages',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PikiPage',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('rel_path', models.CharField(max_length=1000, unique=True)),
|
||||
('page_txt', models.TextField(max_length=50000)),
|
||||
('tags', models.CharField(blank=True, max_length=1000, null=True)),
|
||||
('deleted', models.BooleanField(default=False)),
|
||||
('creation_time', models.DateTimeField(blank=True, null=True)),
|
||||
('modified_time', models.DateTimeField(blank=True, null=True)),
|
||||
('creation_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='creation_user', to=settings.AUTH_USER_MODEL)),
|
||||
('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='group', to='auth.group')),
|
||||
('modified_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='modified_user', to=settings.AUTH_USER_MODEL)),
|
||||
('owner', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owner', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
283
pages/models.py
283
pages/models.py
@ -1,3 +1,284 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext as _
|
||||
from simple_history.models import HistoricalRecords
|
||||
|
||||
# Create your models here.
|
||||
from datetime import datetime
|
||||
import difflib
|
||||
import logging
|
||||
import os
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from users.models import get_userprofile
|
||||
from pages import url_page
|
||||
|
||||
import mycreole
|
||||
|
||||
logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__)
|
||||
|
||||
|
||||
class PikiPage(models.Model):
|
||||
SAVE_ON_CHANGE_FIELDS = ["rel_path", "page_txt", "tags", "deleted", "owner", "group"]
|
||||
#
|
||||
rel_path = models.CharField(unique=True, max_length=1000)
|
||||
page_txt = models.TextField(max_length=50000)
|
||||
tags = models.CharField(max_length=1000, null=True, blank=True)
|
||||
deleted = models.BooleanField(default=False)
|
||||
#
|
||||
creation_time = models.DateTimeField(null=True, blank=True)
|
||||
creation_user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="creation_user")
|
||||
modified_time = models.DateTimeField(null=True, blank=True)
|
||||
modified_user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="modified_user")
|
||||
#
|
||||
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL, related_name="owner")
|
||||
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.SET_NULL, related_name="group")
|
||||
# owner_perms
|
||||
# group_perms
|
||||
# other_perms
|
||||
#
|
||||
history = HistoricalRecords()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def prepare_save(self, request):
|
||||
# Set date
|
||||
tmd = datetime.now(tz=ZoneInfo("UTC")).replace(microsecond=0)
|
||||
self.creation_time = self.creation_time or tmd
|
||||
self.modified_time = tmd
|
||||
# Set user
|
||||
self.creation_user = self.creation_user or request.user
|
||||
self.owner = self.owner or request.user
|
||||
self.modified_user = request.user
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
if self.id and not force_update:
|
||||
orig = PikiPage.objects.get(id=self.id)
|
||||
for key in self.SAVE_ON_CHANGE_FIELDS:
|
||||
if getattr(self, key) != getattr(orig, key):
|
||||
break
|
||||
else:
|
||||
self.save_needed = False
|
||||
return False
|
||||
self.save_needed = True
|
||||
return models.Model.save(self, force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
|
||||
|
||||
#
|
||||
# Set history datetime to modified datetime
|
||||
#
|
||||
@property
|
||||
def _history_date(self):
|
||||
return self.modified_time
|
||||
|
||||
@_history_date.setter
|
||||
def _history_date(self, value):
|
||||
self.modified_time = value
|
||||
|
||||
#
|
||||
# My information
|
||||
#
|
||||
@property
|
||||
def title(self):
|
||||
return self.rel_path.split("/")[-1]
|
||||
|
||||
#
|
||||
# My methods
|
||||
#
|
||||
def render_to_html(self, request, history=None):
|
||||
if history:
|
||||
h = self.history.get(history_id=history)
|
||||
return self.render_text(request, h.page_txt)
|
||||
else:
|
||||
return self.render_text(request, self.page_txt)
|
||||
|
||||
def user_datetime(self, request, dtm):
|
||||
try:
|
||||
up = get_userprofile(request.user)
|
||||
except AttributeError:
|
||||
tz = ZoneInfo("UTC")
|
||||
else:
|
||||
tz = ZoneInfo(up.timezone)
|
||||
#
|
||||
return datetime.astimezone(dtm, tz)
|
||||
|
||||
def render_meta(self, request, history):
|
||||
# Page information
|
||||
meta = f'= {_("Meta data")}\n'
|
||||
meta += f'|{_("Created by")}:|{self.creation_user}|\n'
|
||||
meta += f'|{_("Created at")}:|{self.user_datetime(request, self.creation_time)}|\n'
|
||||
meta += f'|{_("Modified by")}:|{self.modified_user}|\n'
|
||||
meta += f'|{_("Modified at")}:|{self.user_datetime(request, self.modified_time)}|\n'
|
||||
meta += f'|{_("Owner")}:|{self.owner or "---"}|\n'
|
||||
meta += f'|{_("Group")}:|{self.group or "---"}|\n'
|
||||
meta += f'|{_("Tags")}|{self.tags or "---"}|\n'
|
||||
#
|
||||
# List of history page versions
|
||||
#
|
||||
hl = self.history.all()[1:]
|
||||
if len(hl) > 0:
|
||||
meta += f'= {_("History")}\n'
|
||||
meta += f'| ={_("Version")} | ={_("Date")} | ={_("Page")} | ={_("Meta data")} | ={_("Page changed")} | ={_("Tags changed")} | \n'
|
||||
# Current
|
||||
name = _("Current")
|
||||
meta += f"| {name} \
|
||||
| {self.user_datetime(request, self.modified_time)} \
|
||||
| [[{url_page(self.rel_path)} | Page]] \
|
||||
| [[{url_page(self.rel_path, meta=None)} | Meta]] |"
|
||||
page_content = self.page_txt.replace("\r\n", "\n").strip("\n")
|
||||
tags = self.tags
|
||||
for h_page in hl:
|
||||
page_changed = page_content != h_page.page_txt.replace("\r\n", "\n").strip("\n")
|
||||
tags_changed = tags != h_page.tags
|
||||
if page_changed or tags_changed:
|
||||
meta += " %s |" % ("Yes" if page_changed else "No")
|
||||
meta += " %s |" % ("Yes" if tags_changed else "No")
|
||||
meta += "\n"
|
||||
meta += f"| {h_page.history_id} \
|
||||
| {self.user_datetime(request, h_page.modified_time)} \
|
||||
| [[{url_page(self.rel_path, history=h_page.history_id)} | Page]] \
|
||||
| [[{url_page(self.rel_path, meta=None, history=h_page.history_id)} | Meta]] (with diff to current) |"
|
||||
page_content = h_page.page_txt[:].replace("\r\n", "\n").strip("\n")
|
||||
tags = h_page.tags
|
||||
meta += " --- | --- |\n"
|
||||
# Diff
|
||||
html_diff = ""
|
||||
if history:
|
||||
h_page = self.history.get(history_id=history)
|
||||
#
|
||||
meta += f'= {_("Page differences")}\n'
|
||||
#
|
||||
left_lines = self.page_txt.splitlines()
|
||||
right_lines = h_page.page_txt.splitlines()
|
||||
html_diff = difflib.HtmlDiff(wrapcolumn=80).make_table(left_lines, right_lines, "Current page", "Page Version %d" % history)
|
||||
#
|
||||
return mycreole.render_simple(meta) + html_diff
|
||||
|
||||
#
|
||||
# Creole stuff
|
||||
#
|
||||
def render_text(self, request, txt):
|
||||
macros = {
|
||||
"subpages": self.macro_subpages,
|
||||
"allpages": self.macro_allpages,
|
||||
"subpagetree": self.macro_subpagetree,
|
||||
"allpagestree": self.macro_allpagestree,
|
||||
}
|
||||
return mycreole.render(request, txt, self.rel_path, macros=macros)
|
||||
|
||||
def macro_subpages(self, *args, **kwargs):
|
||||
return self.macro_pages(*args, **kwargs)
|
||||
|
||||
def macro_allpages(self, *args, **kwargs):
|
||||
kwargs["allpages"] = True
|
||||
return self.macro_pages(*args, **kwargs)
|
||||
|
||||
def macro_allpagestree(self, *args, **kwargs):
|
||||
kwargs["allpages"] = True
|
||||
kwargs["tree"] = True
|
||||
return self.macro_pages(*args, **kwargs)
|
||||
|
||||
def macro_subpagetree(self, * args, **kwargs):
|
||||
kwargs["tree"] = True
|
||||
return self.macro_pages(*args, **kwargs)
|
||||
|
||||
def macro_pages(self, *args, **kwargs):
|
||||
allpages = kwargs.pop("allpages", False)
|
||||
tree = kwargs.pop("tree", False)
|
||||
#
|
||||
|
||||
def parse_depth(s: str):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
params = kwargs.get('', '')
|
||||
filter_str = ''
|
||||
depth = parse_depth(params)
|
||||
if depth is None:
|
||||
params = params.split(",")
|
||||
depth = parse_depth(params[0])
|
||||
if len(params) == 2:
|
||||
filter_str = params[1]
|
||||
elif depth is None:
|
||||
filter_str = params[0]
|
||||
#
|
||||
if not allpages:
|
||||
filter_str = os.path.join(self.rel_path, filter_str)
|
||||
#
|
||||
pages = PikiPage.objects.filter(rel_path__contains=filter_str)
|
||||
pl = page_list([p for p in pages if not p.deleted])
|
||||
#
|
||||
if tree:
|
||||
return "<pre>\n" + page_tree(pl).html() + "</pre>\n"
|
||||
else:
|
||||
return pl.html_list(depth=depth, filter_str=filter_str, parent_rel_path='' if allpages else self.rel_path)
|
||||
|
||||
|
||||
class page_list(list):
|
||||
def __init__(self, *args, **kwargs):
|
||||
return super().__init__(*args, **kwargs)
|
||||
|
||||
def sort_basename(self):
|
||||
return list.sort(self, key=lambda x: os.path.basename(x.rel_path))
|
||||
|
||||
def creole_list(self, depth=None, filter_str='', parent_rel_path=''):
|
||||
self.sort_basename()
|
||||
depth = depth or 9999 # set a random high value if None
|
||||
#
|
||||
rv = ""
|
||||
last_char = None
|
||||
for page in self:
|
||||
if page.rel_path.startswith(filter_str) and page.rel_path != filter_str:
|
||||
name = page.rel_path[len(parent_rel_path):].lstrip("/")
|
||||
if name.count('/') < depth:
|
||||
first_char = os.path.basename(name)[0].upper()
|
||||
if last_char != first_char:
|
||||
last_char = first_char
|
||||
rv += f"=== {first_char}\n"
|
||||
rv += f"* [[{url_page(page.rel_path)} | {name} ]]\n"
|
||||
return rv
|
||||
|
||||
def html_list(self, depth=9999, filter_str='', parent_rel_path=''):
|
||||
return mycreole.render_simple(self.creole_list(depth, filter_str, parent_rel_path))
|
||||
|
||||
|
||||
class page_tree(dict):
|
||||
T_PATTERN = "├── "
|
||||
L_PATTERN = "└── "
|
||||
I_PATTERN = "│ "
|
||||
D_PATTERN = " "
|
||||
|
||||
def __init__(self, pl: page_list):
|
||||
super().__init__()
|
||||
for page in pl:
|
||||
store_item = self
|
||||
for entry in page.rel_path.split("/"):
|
||||
if not entry in store_item:
|
||||
store_item[entry] = {}
|
||||
store_item = store_item[entry]
|
||||
|
||||
def html(self, rel_path=None, fill=""):
|
||||
base = self
|
||||
try:
|
||||
for key in rel_path.split("/"):
|
||||
base = base[key]
|
||||
except AttributeError:
|
||||
rel_path = ''
|
||||
#
|
||||
rv = ""
|
||||
#
|
||||
l = len(base)
|
||||
for entry in sorted(list(base.keys())):
|
||||
l -= 1
|
||||
page_path = os.path.join(rel_path, entry)
|
||||
try:
|
||||
PikiPage.objects.get(rel_path=page_path)
|
||||
except PikiPage.DoesNotExist:
|
||||
pass
|
||||
else:
|
||||
entry = f'<a href="{url_page(page_path)}">{entry}</a>'
|
||||
rv += fill + (self.L_PATTERN if l == 0 else self.T_PATTERN) + entry + "\n"
|
||||
rv += self.html(page_path, fill=fill+(self.D_PATTERN if l == 0 else self.I_PATTERN))
|
||||
return rv
|
||||
|
267
pages/page.py
267
pages/page.py
@ -30,22 +30,27 @@ def full_path_all_pages(expression="*"):
|
||||
return rv
|
||||
|
||||
|
||||
def full_path_deleted_pages(expression="*"):
|
||||
system_pages = fstools.dirlist(settings.SYSTEM_PAGES_ROOT, expression=expression, rekursive=False)
|
||||
system_pages = [os.path.join(settings.PAGES_ROOT, os.path.basename(path)) for path in system_pages]
|
||||
pages = fstools.dirlist(settings.PAGES_ROOT, expression=expression, rekursive=False)
|
||||
rv = []
|
||||
for path in set(system_pages + pages):
|
||||
p = page_wrapped(None, path)
|
||||
if not p.is_available():
|
||||
rv.append(path)
|
||||
return rv
|
||||
class base(dict):
|
||||
@property
|
||||
def rel_path(self):
|
||||
return os.path.basename(self._path).replace(2*SPLITCHAR, "/")
|
||||
|
||||
def is_available(self):
|
||||
is_a = os.path.isfile(self.filename)
|
||||
if not is_a:
|
||||
logger.debug("Not available - %s", self.filename)
|
||||
return is_a
|
||||
|
||||
def history_numbers_list(self):
|
||||
history_folder = os.path.join(self._path, HISTORY_FOLDER_NAME)
|
||||
return list(set([int(os.path.basename(filename)[:5]) for filename in fstools.filelist(history_folder)]))
|
||||
|
||||
|
||||
class meta_data(dict):
|
||||
class meta_data(base):
|
||||
META_FILE_NAME = 'meta.json'
|
||||
#
|
||||
KEY_CREATION_TIME = "creation_time"
|
||||
KEY_CREATION_USER = "creation_user"
|
||||
KEY_MODIFIED_TIME = "modified_time"
|
||||
KEY_MODIFIED_USER = "modified_user"
|
||||
KEY_TAGS = "tags"
|
||||
@ -85,6 +90,9 @@ class meta_data(dict):
|
||||
if username:
|
||||
self[self.KEY_MODIFIED_TIME] = int(time.time())
|
||||
self[self.KEY_MODIFIED_USER] = username
|
||||
#
|
||||
if self.KEY_CREATION_USER not in self:
|
||||
self[self.KEY_CREATION_USER] = self[self.KEY_MODIFIED_USER]
|
||||
if self.KEY_CREATION_TIME not in self:
|
||||
self[self.KEY_CREATION_TIME] = self[self.KEY_MODIFIED_TIME]
|
||||
if tags:
|
||||
@ -109,7 +117,7 @@ class meta_data(dict):
|
||||
shutil.copy(self.filename, history_filename)
|
||||
|
||||
|
||||
class page_data(object):
|
||||
class page_data(base):
|
||||
PAGE_FILE_NAME = 'page'
|
||||
|
||||
def __init__(self, path, history_version=None):
|
||||
@ -168,12 +176,6 @@ class page_data(object):
|
||||
def rel_path(self):
|
||||
return os.path.basename(self._path).replace(2*SPLITCHAR, "/")
|
||||
|
||||
def is_available(self):
|
||||
is_a = os.path.isfile(self.filename)
|
||||
if not is_a:
|
||||
logger.debug("page.is_available: Not available - %s", self.filename)
|
||||
return is_a
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
return os.path.basename(self._path).split(2*SPLITCHAR)[-1]
|
||||
@ -189,200 +191,6 @@ class page_data(object):
|
||||
shutil.copy(self.filename, history_filename)
|
||||
|
||||
|
||||
class page_django(page_data):
|
||||
FOLDER_ATTACHMENTS = "attachments"
|
||||
|
||||
def __init__(self, request, path, history_version=None) -> None:
|
||||
self._request = request
|
||||
super().__init__(path, history_version=history_version)
|
||||
|
||||
@property
|
||||
def attachment_path(self):
|
||||
return os.path.join(os.path.basename(self._path), self.FOLDER_ATTACHMENTS)
|
||||
|
||||
def render_to_html(self):
|
||||
if self.is_available():
|
||||
return self.render_text(self._request, self.raw_page_src)
|
||||
else:
|
||||
messages.unavailable_msg_page(self._request, self.rel_path)
|
||||
return ""
|
||||
|
||||
def history_numbers_list(self):
|
||||
history_folder = os.path.join(self._path, HISTORY_FOLDER_NAME)
|
||||
return list(set([int(os.path.basename(filename)[:5]) for filename in fstools.filelist(history_folder)]))
|
||||
|
||||
def render_meta(self, ctime, mtime, user, tags):
|
||||
#
|
||||
# Page meta data
|
||||
#
|
||||
meta = f'=== {_("Meta data")}\n'
|
||||
meta += f'|{_("Created")}:|{timestamp_to_datetime(self._request, ctime)}|\n'
|
||||
meta += f'|{_("Modified")}:|{timestamp_to_datetime(self._request, mtime)}|\n'
|
||||
meta += f'|{_("Editor")}|{user}|\n'
|
||||
meta += f'|{_("Tags")}|{tags}|\n'
|
||||
#
|
||||
# List of hostory page versions
|
||||
#
|
||||
hnl = self.history_numbers_list()
|
||||
if hnl:
|
||||
meta += f'=== {_("History")}\n'
|
||||
meta += f'| ={_("Version")} | ={_("Date")} | ={_("Page")} | ={_("Meta data")} | \n'
|
||||
# Current
|
||||
name = _("Current")
|
||||
meta += f"| {name} \
|
||||
| {timestamp_to_datetime(self._request, mtime)} \
|
||||
| [[{url_page(self.rel_path)} | Page]] \
|
||||
| [[{url_page(self.rel_path, meta=None)} | Meta]]\n"
|
||||
# History
|
||||
for num in reversed(hnl):
|
||||
p = page_wrapped(self._request, self._path, history_version=num)
|
||||
meta += f"| {num} \
|
||||
| {timestamp_to_datetime(self._request, p.modified_time)} \
|
||||
| [[{url_page(p.rel_path, history=num)} | Page]] \
|
||||
| [[{url_page(p.rel_path, meta=None, history=num)} | Meta]] (with page changes)\n"
|
||||
# Diff
|
||||
html_diff = ""
|
||||
if self._history_version:
|
||||
meta += f'=== {_("Page differences")}\n'
|
||||
#
|
||||
c = page_django(self._request, self._path)
|
||||
left_lines = c.raw_page_src.splitlines()
|
||||
right_lines = self.raw_page_src.splitlines()
|
||||
html_diff = difflib.HtmlDiff(wrapcolumn=80).make_table(left_lines, right_lines)
|
||||
#
|
||||
return mycreole.render_simple(meta) + html_diff
|
||||
|
||||
def render_text(self, request, txt):
|
||||
macros = {
|
||||
"subpages": self.macro_subpages,
|
||||
"allpages": self.macro_allpages,
|
||||
"subpagetree": self.macro_subpagetree,
|
||||
"allpagestree": self.macro_allpagestree,
|
||||
}
|
||||
return mycreole.render(request, txt, self.attachment_path, macros=macros)
|
||||
|
||||
def macro_allpages(self, *args, **kwargs):
|
||||
kwargs["allpages"] = True
|
||||
return self.macro_subpages(*args, **kwargs)
|
||||
|
||||
def macro_subpages(self, *args, **kwargs):
|
||||
allpages = kwargs.pop("allpages", False)
|
||||
tree = kwargs.pop("tree", False)
|
||||
#
|
||||
|
||||
def parse_depth(s: str):
|
||||
try:
|
||||
return int(s)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
params = kwargs.get('', '')
|
||||
filter_str = ''
|
||||
depth = parse_depth(params)
|
||||
if depth is None:
|
||||
params = params.split(",")
|
||||
depth = parse_depth(params[0])
|
||||
if len(params) == 2:
|
||||
filter_str = params[1]
|
||||
elif depth is None:
|
||||
filter_str = params[0]
|
||||
#
|
||||
rv = ""
|
||||
# create a page_list
|
||||
if allpages:
|
||||
expression = "*"
|
||||
parent_rel_path = ""
|
||||
else:
|
||||
expression = os.path.basename(self._path) + 2 * SPLITCHAR + "*"
|
||||
parent_rel_path = self.rel_path
|
||||
#
|
||||
pl = page_list(
|
||||
self._request,
|
||||
[page_django(self._request, path) for path in full_path_all_pages(expression)]
|
||||
)
|
||||
if tree:
|
||||
return "<pre>\n" + page_tree(pl).html() + "</pre>\n"
|
||||
else:
|
||||
return pl.html_list(depth=depth, filter_str=filter_str, parent_rel_path=parent_rel_path)
|
||||
|
||||
def macro_allpagestree(self, *args, **kwargs):
|
||||
kwargs["allpages"] = True
|
||||
kwargs["tree"] = True
|
||||
return self.macro_subpages(*args, **kwargs)
|
||||
|
||||
def macro_subpagetree(self, * args, **kwargs):
|
||||
kwargs["tree"] = True
|
||||
return self.macro_subpages(*args, **kwargs)
|
||||
|
||||
|
||||
class page_list(list):
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
self._request = request
|
||||
return super().__init__(*args, **kwargs)
|
||||
|
||||
def sort_basename(self):
|
||||
return list.sort(self, key=lambda x: os.path.basename(x.rel_path))
|
||||
|
||||
def creole_list(self, depth=None, filter_str='', parent_rel_path=''):
|
||||
self.sort_basename()
|
||||
depth = depth or 9999 # set a random high value if None
|
||||
#
|
||||
parent_rel_path = parent_rel_path + "/" if len(parent_rel_path) > 0 else ""
|
||||
#
|
||||
rv = ""
|
||||
last_char = None
|
||||
for page in self:
|
||||
name = page.rel_path[len(parent_rel_path):]
|
||||
if name.startswith(filter_str) and name != filter_str:
|
||||
if name.count('/') < depth:
|
||||
first_char = os.path.basename(name)[0].upper()
|
||||
if last_char != first_char:
|
||||
last_char = first_char
|
||||
rv += f"=== {first_char}\n"
|
||||
rv += f"* [[{url_page(page.rel_path)} | {name} ]]\n"
|
||||
return rv
|
||||
|
||||
def html_list(self, depth=9999, filter_str='', parent_rel_path=''):
|
||||
return mycreole.render_simple(self.creole_list(depth, filter_str, parent_rel_path))
|
||||
|
||||
|
||||
class page_tree(dict):
|
||||
T_PATTERN = "├── "
|
||||
L_PATTERN = "└── "
|
||||
I_PATTERN = "│ "
|
||||
D_PATTERN = " "
|
||||
|
||||
def __init__(self, pl: page_list):
|
||||
super().__init__()
|
||||
for page in pl:
|
||||
store_item = self
|
||||
for entry in page.rel_path.split("/"):
|
||||
if not entry in store_item:
|
||||
store_item[entry] = {}
|
||||
store_item = store_item[entry]
|
||||
|
||||
def html(self, rel_path=None, fill=""):
|
||||
base = self
|
||||
try:
|
||||
for key in rel_path.split("/"):
|
||||
base = base[key]
|
||||
except AttributeError:
|
||||
rel_path = ''
|
||||
#
|
||||
rv = ""
|
||||
#
|
||||
l = len(base)
|
||||
for entry in sorted(list(base.keys())):
|
||||
l -= 1
|
||||
page_path = os.path.join(rel_path, entry)
|
||||
page = page_wrapped(None, page_path)
|
||||
if page.is_available():
|
||||
entry = f'<a href="{url_page(page_path)}">{entry}</a>'
|
||||
rv += fill + (self.L_PATTERN if l == 0 else self.T_PATTERN) + entry + "\n"
|
||||
rv += self.html(page_path, fill=fill+(self.D_PATTERN if l == 0 else self.I_PATTERN))
|
||||
return rv
|
||||
|
||||
|
||||
class page_wrapped(object):
|
||||
"""
|
||||
This class holds different page and meta instances and decides which will be used in which case.
|
||||
@ -399,19 +207,9 @@ class page_wrapped(object):
|
||||
self._request = request
|
||||
#
|
||||
page_path = self.__page_path__(path)
|
||||
system_page_path = self.__system_page_path__(path)
|
||||
# Page
|
||||
if request:
|
||||
self._page = page_django(request, page_path, history_version=history_version)
|
||||
else:
|
||||
self._page = page_data(page_path, history_version=history_version)
|
||||
self._page = page_data(page_path, history_version=history_version)
|
||||
self._page_meta = meta_data(page_path, history_version=history_version)
|
||||
# System page
|
||||
if request:
|
||||
self._system_page = page_django(request, system_page_path)
|
||||
else:
|
||||
self._system_page = page_data(system_page_path)
|
||||
self._system_meta_data = meta_data(system_page_path)
|
||||
|
||||
def __page_path__(self, path):
|
||||
if path.startswith(settings.PAGES_ROOT):
|
||||
@ -421,20 +219,11 @@ class page_wrapped(object):
|
||||
# must be a relative url
|
||||
return os.path.join(settings.PAGES_ROOT, path.replace("/", 2*SPLITCHAR))
|
||||
|
||||
def __system_page_path__(self, path):
|
||||
return os.path.join(settings.SYSTEM_PAGES_ROOT, os.path.basename(path))
|
||||
|
||||
def __page_choose__(self):
|
||||
if not self._page.is_available():
|
||||
return self._system_page
|
||||
else:
|
||||
return self._page
|
||||
return self._page
|
||||
|
||||
def __meta_choose__(self):
|
||||
if not self._page.is_available():
|
||||
return self._system_meta_data
|
||||
else:
|
||||
return self._page_meta
|
||||
return self._page_meta
|
||||
|
||||
def __store_history__(self):
|
||||
if self._page.is_available():
|
||||
@ -454,6 +243,12 @@ class page_wrapped(object):
|
||||
rv = meta.get(meta.KEY_CREATION_TIME)
|
||||
return rv
|
||||
|
||||
@property
|
||||
def creation_user(self):
|
||||
meta = self.__meta_choose__()
|
||||
rv = meta.get(meta.KEY_CREATION_USER)
|
||||
return rv
|
||||
|
||||
def delete(self):
|
||||
self.__store_history__()
|
||||
self._page.delete()
|
||||
@ -490,7 +285,7 @@ class page_wrapped(object):
|
||||
return rv
|
||||
|
||||
def is_available(self):
|
||||
return self._page.is_available() or self._system_page.is_available()
|
||||
return self._page.is_available()
|
||||
|
||||
def userpage_is_available(self):
|
||||
return self._page.is_available()
|
||||
@ -509,7 +304,7 @@ class page_wrapped(object):
|
||||
|
||||
def render_meta(self):
|
||||
page = self.__page_choose__()
|
||||
rv = page.render_meta(self.creation_time, self.modified_time, self.modified_user, self.tags)
|
||||
rv = page.render_meta(self.creation_time, self.modified_time, self.creation_user, self.modified_user, self.tags)
|
||||
return rv
|
||||
|
||||
def render_to_html(self):
|
||||
|
@ -8,7 +8,7 @@ from whoosh.fields import Schema, ID, TEXT, DATETIME
|
||||
from whoosh.qparser.dateparse import DateParserPlugin
|
||||
from whoosh import index, qparser
|
||||
|
||||
from pages.page import page_wrapped, full_path_all_pages
|
||||
from .models import PikiPage
|
||||
|
||||
logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__)
|
||||
|
||||
@ -38,11 +38,11 @@ def create_index():
|
||||
|
||||
|
||||
def rebuild_index(ix):
|
||||
page_path = full_path_all_pages()
|
||||
for path in page_path:
|
||||
pw = page_wrapped(None, path)
|
||||
add_item(ix, pw)
|
||||
return len(page_path)
|
||||
pages = PikiPage.objects.all()
|
||||
for pp in pages:
|
||||
if not pp.deleted:
|
||||
add_item(ix, pp)
|
||||
return len(pages)
|
||||
|
||||
|
||||
def load_index():
|
||||
@ -56,19 +56,19 @@ def load_index():
|
||||
return ix
|
||||
|
||||
|
||||
def add_item(ix, pw: page_wrapped):
|
||||
def add_item(ix, pp: PikiPage):
|
||||
# Define Standard data
|
||||
#
|
||||
data = dict(
|
||||
id=pw.rel_path,
|
||||
id=pp.rel_path,
|
||||
#
|
||||
title=pw.title,
|
||||
page_src=pw.raw_page_src,
|
||||
tag=pw.tags,
|
||||
title=pp.title,
|
||||
page_src=pp.page_txt,
|
||||
tag=pp.tags,
|
||||
#
|
||||
creation_time=datetime.fromtimestamp(pw.creation_time),
|
||||
modified_time=datetime.fromtimestamp(pw.modified_time),
|
||||
modified_user=pw.modified_user
|
||||
creation_time=pp.creation_time,
|
||||
modified_time=pp.modified_time,
|
||||
modified_user=None if pp.modified_user is None else pp.modified_user.username
|
||||
)
|
||||
with ix.writer() as w:
|
||||
logger.info('Adding document with id=%s to the search index.', data.get('id'))
|
||||
@ -95,13 +95,13 @@ def whoosh_search(search_txt):
|
||||
return rpl
|
||||
|
||||
|
||||
def delete_item(ix, pw: page_wrapped):
|
||||
def delete_item(ix, pp: PikiPage):
|
||||
with ix.writer() as w:
|
||||
logger.info('Removing document with id=%s from the search index.', pw.rel_path)
|
||||
w.delete_by_term("id", pw.rel_path)
|
||||
logger.info('Removing document with id=%s from the search index.', pp.rel_path)
|
||||
w.delete_by_term("id", pp.rel_path)
|
||||
|
||||
|
||||
def update_item(pw: page_wrapped):
|
||||
def update_item(pp: PikiPage):
|
||||
ix = load_index()
|
||||
delete_item(ix, pw)
|
||||
add_item(ix, pw)
|
||||
delete_item(ix, pp)
|
||||
add_item(ix, pp)
|
||||
|
150
pages/views.py
150
pages/views.py
@ -6,7 +6,8 @@ from django.utils.translation import gettext as _
|
||||
|
||||
import logging
|
||||
|
||||
from . import access
|
||||
|
||||
from .access import access_control
|
||||
from . import messages
|
||||
from . import url_page
|
||||
from . import get_search_query
|
||||
@ -14,14 +15,25 @@ import config
|
||||
from .context import context_adaption
|
||||
from .forms import EditForm, RenameForm
|
||||
from .help import help_pages
|
||||
from .models import PikiPage, page_list
|
||||
import mycreole
|
||||
from .page import page_wrapped, page_list
|
||||
from .search import whoosh_search, load_index, delete_item, add_item
|
||||
from .search import whoosh_search, load_index, delete_item, add_item, update_item
|
||||
from themes import Context
|
||||
|
||||
logger = logging.getLogger(settings.ROOT_LOGGER_NAME).getChild(__name__)
|
||||
|
||||
|
||||
SUCCESS_PAGE = _("""= Default startpage
|
||||
**Congratulations!!!**
|
||||
|
||||
Seeing this page means, that you installed Piki successfull.
|
||||
|
||||
Edit this page to get your own first startpage.
|
||||
|
||||
If you need need assistance to edit a page, visit the [[/helpview/main|help pages]].
|
||||
""")
|
||||
|
||||
|
||||
def root(request):
|
||||
return HttpResponseRedirect(url_page(config.STARTPAGE))
|
||||
|
||||
@ -29,19 +41,36 @@ def root(request):
|
||||
def page(request, rel_path):
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
#
|
||||
try:
|
||||
p = PikiPage.objects.get(rel_path=rel_path)
|
||||
except PikiPage.DoesNotExist:
|
||||
p = None
|
||||
meta = "meta" in request.GET
|
||||
history = request.GET.get("history")
|
||||
if history:
|
||||
history = int(history)
|
||||
#
|
||||
p = page_wrapped(request, rel_path, history_version=history)
|
||||
if access.read_page(request, rel_path):
|
||||
if meta:
|
||||
page_content = p.render_meta()
|
||||
title = rel_path.split("/")[-1]
|
||||
#
|
||||
acc = access_control(request, rel_path)
|
||||
if acc.may_read():
|
||||
if p is None or p.deleted:
|
||||
if rel_path == config.STARTPAGE:
|
||||
page_content = mycreole.render_simple(SUCCESS_PAGE)
|
||||
else:
|
||||
page_content = ""
|
||||
if p is not None and p.deleted:
|
||||
messages.deleted_page(request)
|
||||
else:
|
||||
messages.unavailable_msg_page(request, rel_path)
|
||||
else:
|
||||
page_content = p.render_to_html()
|
||||
if history:
|
||||
messages.history_version_display(request, rel_path, history)
|
||||
title = p.title
|
||||
if meta:
|
||||
page_content = p.render_meta(request, history)
|
||||
else:
|
||||
page_content = p.render_to_html(request, history)
|
||||
if history:
|
||||
messages.history_version_display(request, rel_path, history)
|
||||
else:
|
||||
messages.permission_denied_msg_page(request, rel_path)
|
||||
page_content = ""
|
||||
@ -50,65 +79,75 @@ def page(request, rel_path):
|
||||
context,
|
||||
request,
|
||||
rel_path=rel_path,
|
||||
title=p.title,
|
||||
upload_path=p.attachment_path,
|
||||
title=title,
|
||||
upload_path=rel_path,
|
||||
page_content=page_content,
|
||||
is_available=p.userpage_is_available()
|
||||
is_available=p is not None and not p.deleted
|
||||
)
|
||||
return render(request, 'pages/page.html', context=context)
|
||||
|
||||
|
||||
def edit(request, rel_path):
|
||||
if access.write_page(request, rel_path):
|
||||
acc = access_control(request, rel_path)
|
||||
if acc.may_write():
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
#
|
||||
try:
|
||||
p = PikiPage.objects.get(rel_path=rel_path)
|
||||
is_available = True
|
||||
except PikiPage.DoesNotExist:
|
||||
p = PikiPage(rel_path=rel_path)
|
||||
is_available = False
|
||||
#
|
||||
if not request.POST:
|
||||
history = request.GET.get("history")
|
||||
if history:
|
||||
history = int(history)
|
||||
#
|
||||
p = page_wrapped(request, rel_path, history_version=history)
|
||||
#
|
||||
form = EditForm(page_data=p.raw_page_src, page_tags=p.tags)
|
||||
form = EditForm(instance=p.history.get(history_id=history))
|
||||
else:
|
||||
form = EditForm(instance=p)
|
||||
#
|
||||
context_adaption(
|
||||
context,
|
||||
request,
|
||||
rel_path=rel_path,
|
||||
is_available=p.userpage_is_available(),
|
||||
is_available=is_available,
|
||||
form=form,
|
||||
# TODO: Add translation
|
||||
title=_("Edit page %s") % repr(p.title),
|
||||
upload_path=p.attachment_path,
|
||||
upload_path=rel_path,
|
||||
)
|
||||
return render(request, 'pages/page_edit.html', context=context)
|
||||
else:
|
||||
p = page_wrapped(request, rel_path)
|
||||
form = EditForm(request.POST, instance=p)
|
||||
#
|
||||
save = request.POST.get("save")
|
||||
page_txt = request.POST.get("page_txt")
|
||||
tags = request.POST.get("page_tags")
|
||||
preview = request.POST.get("preview")
|
||||
#
|
||||
if save is not None:
|
||||
if p.update_page(page_txt, tags):
|
||||
messages.edit_success(request)
|
||||
if form.is_valid():
|
||||
form.instance.prepare_save(request)
|
||||
page = form.save()
|
||||
if page.save_needed:
|
||||
messages.edit_success(request)
|
||||
# update search index
|
||||
update_item(page)
|
||||
else:
|
||||
messages.no_change(request)
|
||||
else:
|
||||
messages.no_change(request)
|
||||
messages.internal_error(request)
|
||||
return HttpResponseRedirect(url_page(rel_path))
|
||||
elif preview is not None:
|
||||
form = EditForm(page_data=page_txt, page_tags=tags)
|
||||
#
|
||||
context_adaption(
|
||||
context,
|
||||
request,
|
||||
rel_path=rel_path,
|
||||
is_available=p.userpage_is_available(),
|
||||
is_available=is_available,
|
||||
form=form,
|
||||
# TODO: Add translation
|
||||
title=_("Edit page %s") % repr(p.title),
|
||||
upload_path=p.attachment_path,
|
||||
page_content=p.render_text(request, page_txt)
|
||||
upload_path=rel_path,
|
||||
page_content=p.render_text(request, form.data.get("page_txt"))
|
||||
)
|
||||
return render(request, 'pages/page_edit.html', context=context)
|
||||
else:
|
||||
@ -119,11 +158,18 @@ def edit(request, rel_path):
|
||||
|
||||
|
||||
def delete(request, rel_path):
|
||||
if access.write_page(request, rel_path):
|
||||
acc = access_control(request, rel_path)
|
||||
if acc.may_write():
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
#
|
||||
try:
|
||||
p = PikiPage.objects.get(rel_path=rel_path)
|
||||
is_available = True
|
||||
except PikiPage.DoesNotExist:
|
||||
p = PikiPage(rel_path=rel_path)
|
||||
is_available = False
|
||||
#
|
||||
if not request.POST:
|
||||
p = page_wrapped(request, rel_path)
|
||||
#
|
||||
# form = DeleteForm(page_data=p.raw_page_src, page_tags=p.tags)
|
||||
#
|
||||
@ -131,24 +177,21 @@ def delete(request, rel_path):
|
||||
context,
|
||||
request,
|
||||
rel_path=rel_path,
|
||||
is_available=p.userpage_is_available(),
|
||||
# form=form,
|
||||
is_available=is_available,
|
||||
# TODO: Add translation
|
||||
title=_("Delete page %s") % repr(p.title),
|
||||
upload_path=p.attachment_path,
|
||||
page_content=p.render_to_html(),
|
||||
upload_path=rel_path,
|
||||
page_content=p.render_to_html(request),
|
||||
)
|
||||
else:
|
||||
p = page_wrapped(request, rel_path)
|
||||
#
|
||||
delete = request.POST.get("delete")
|
||||
#
|
||||
if delete:
|
||||
p.deleted = True
|
||||
p.save()
|
||||
# delete page from search index
|
||||
ix = load_index()
|
||||
delete_item(ix, p)
|
||||
# delete move files to history
|
||||
p.delete()
|
||||
# add delete message
|
||||
messages.page_deleted(request, p.title)
|
||||
return HttpResponseRedirect("/")
|
||||
@ -162,28 +205,32 @@ def delete(request, rel_path):
|
||||
|
||||
|
||||
def rename(request, rel_path):
|
||||
if access.write_page(request, rel_path):
|
||||
acc = access_control(request, rel_path)
|
||||
if acc.may_write():
|
||||
context = Context(request) # needs to be executed first because of time mesurement
|
||||
#
|
||||
try:
|
||||
p = PikiPage.objects.get(rel_path=rel_path)
|
||||
is_available = True
|
||||
except PikiPage.DoesNotExist:
|
||||
p = PikiPage(rel_path=rel_path)
|
||||
is_available = False
|
||||
#
|
||||
if not request.POST:
|
||||
p = page_wrapped(request, rel_path)
|
||||
#
|
||||
form = RenameForm(page_name=p.rel_path)
|
||||
#
|
||||
context_adaption(
|
||||
context,
|
||||
request,
|
||||
rel_path=rel_path,
|
||||
is_available=p.userpage_is_available(),
|
||||
is_available=is_available,
|
||||
form=form,
|
||||
# TODO: Add translation
|
||||
title=_("Delete page %s") % repr(p.title),
|
||||
upload_path=p.attachment_path,
|
||||
page_content=p.render_to_html(),
|
||||
upload_path=rel_path,
|
||||
page_content=p.render_to_html(request),
|
||||
)
|
||||
else:
|
||||
p = page_wrapped(request, rel_path)
|
||||
#
|
||||
rename = request.POST.get("rename")
|
||||
page_name = request.POST.get("page_name")
|
||||
if rename:
|
||||
@ -194,7 +241,8 @@ def rename(request, rel_path):
|
||||
ix = load_index()
|
||||
delete_item(ix, p)
|
||||
# rename the storage folder
|
||||
p.rename(page_name)
|
||||
p.rel_path = page_name
|
||||
p.save()
|
||||
# add the renamed page to the search index
|
||||
add_item(ix, p)
|
||||
# add rename message
|
||||
@ -217,7 +265,7 @@ def search(request):
|
||||
if sr is None:
|
||||
django_messages.error(request, _('Invalid search pattern: %s') % repr(search_txt))
|
||||
sr = []
|
||||
pl = page_list(request, [page_wrapped(request, rel_path) for rel_path in set(sr)])
|
||||
pl = page_list([PikiPage.objects.get(rel_path=rel_path) for rel_path in set(sr)])
|
||||
#
|
||||
context_adaption(
|
||||
context,
|
||||
|
@ -41,6 +41,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
#
|
||||
'simple_history',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@ -124,7 +126,7 @@ STATIC_URL = 'static/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'data', 'media')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
MYCREOLE_ROOT = os.path.join(BASE_DIR, 'data', 'pages')
|
||||
MYCREOLE_ROOT = os.path.join(BASE_DIR, 'data', 'mycreole')
|
||||
MYCREOLE_ATTACHMENT_ACCESS = {
|
||||
'read': 'pages.access.read_attachment',
|
||||
'modify': 'pages.access.modify_attachment',
|
||||
|
Loading…
x
Reference in New Issue
Block a user