1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-09 10:50:24 +00:00

Modified wiki, slugified page link, ignored case when parsing page link.

This commit is contained in:
zhengxie
2013-04-04 19:57:52 +08:00
parent f523998daf
commit a1c526b7e7
6 changed files with 122 additions and 75 deletions

View File

@@ -110,6 +110,13 @@ addConfirmTo($('#page-delete'), {
'title': 'Delete Page', 'title': 'Delete Page',
'con': 'Are you sure you want to delete this page?' 'con': 'Are you sure you want to delete this page?'
}); });
$('a.wiki-page-missing').each(function(){
$(this).click(function() {
$('#page-name').val($(this).text());
$('#page-create-form').modal({appendTo: '#main', autoResize: true});
return false;
});
});
{% else %} {% else %}
$('#wiki-create').click(function() { $('#wiki-create').click(function() {

View File

@@ -38,8 +38,8 @@
</div> </div>
<ul id="wiki-pages"> <ul id="wiki-pages">
{% for page in pages %} {% for page_slug, page in pages.items %}
<li><a href="{% url 'group_wiki' group.id page %}">{{ page }}</a></li> <li><a href="{% url 'group_wiki' group.id page_slug %}">{{ page }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@@ -11,26 +11,23 @@ from django.contrib import messages
from django.contrib.sites.models import RequestSite from django.contrib.sites.models import RequestSite
from django.http import HttpResponse, HttpResponseRedirect, Http404, \ from django.http import HttpResponse, HttpResponseRedirect, Http404, \
HttpResponseBadRequest HttpResponseBadRequest
from django.shortcuts import render_to_response, redirect from django.shortcuts import render_to_response
from django.template import Context, loader, RequestContext from django.template import Context, loader, RequestContext
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils import datetime_safe from django.utils import datetime_safe
from django.utils.hashcompat import md5_constructor from django.utils.hashcompat import md5_constructor
from django.utils.http import urlquote
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils.translation import ungettext from django.utils.translation import ungettext
from django.views.generic.base import TemplateResponseMixin
from django.views.generic.edit import BaseFormView, FormMixin
from auth.decorators import login_required from auth.decorators import login_required
import seaserv import seaserv
from seaserv import ccnet_rpc, ccnet_threaded_rpc, seafserv_threaded_rpc, \ from seaserv import ccnet_threaded_rpc, seafserv_threaded_rpc, seafserv_rpc, \
seafserv_rpc, web_get_access_token, \ web_get_access_token, \
get_repo, get_group_repos, get_commits, is_group_user, \ get_repo, get_group_repos, get_commits, is_group_user, \
get_personal_groups_by_user, get_group, get_group_members, create_repo, \ get_personal_groups_by_user, get_group, get_group_members, create_repo, \
get_personal_groups, create_org_repo, get_org_group_repos, \ get_personal_groups, create_org_repo, get_org_group_repos, \
get_org_groups_by_user, check_permission, is_passwd_set, remove_repo, \ check_permission, is_passwd_set, remove_repo, \
unshare_group_repo, get_file_id_by_path, post_empty_file, del_file unshare_group_repo, get_file_id_by_path, post_empty_file, del_file
from pysearpc import SearpcError from pysearpc import SearpcError
@@ -42,24 +39,21 @@ from forms import MessageForm, MessageReplyForm, GroupRecommendForm, \
from signals import grpmsg_added, grpmsg_reply_added from signals import grpmsg_added, grpmsg_reply_added
from settings import GROUP_MEMBERS_DEFAULT_DISPLAY from settings import GROUP_MEMBERS_DEFAULT_DISPLAY
from base.decorators import sys_staff_required from base.decorators import sys_staff_required
from base.mixins import LoginRequiredMixin
from base.models import FileDiscuss from base.models import FileDiscuss
from seahub.contacts.models import Contact from seahub.contacts.models import Contact
from seahub.contacts.signals import mail_sended from seahub.contacts.signals import mail_sended
from seahub.notifications.models import UserNotification from seahub.notifications.models import UserNotification
from seahub.profile.models import Profile
from seahub.settings import SITE_ROOT, SITE_NAME, MEDIA_URL from seahub.settings import SITE_ROOT, SITE_NAME, MEDIA_URL
from seahub.shortcuts import get_first_object_or_none from seahub.shortcuts import get_first_object_or_none
from seahub.utils import render_error, render_permission_error, \ from seahub.utils import render_error, render_permission_error, string2list, \
validate_group_name, string2list, check_and_get_org_by_group, \ check_and_get_org_by_group, gen_file_get_url, get_file_type_and_ext, \
check_and_get_org_by_repo, gen_file_get_url, get_file_type_and_ext, \
get_file_contributors get_file_contributors
from seahub.utils.paginator import Paginator
from seahub.utils.slugify import slugify
from seahub.utils.file_types import IMAGE from seahub.utils.file_types import IMAGE
from seahub.utils import calc_file_path_hash from seahub.utils import calc_file_path_hash
from seahub.utils.paginator import Paginator
from seahub.utils.wiki import normalize_page_name, clean_page_name, get_wiki_dirent
from seahub.views import is_registered_user from seahub.views import is_registered_user
from seahub.forms import RepoCreateForm, SharedRepoCreateForm from seahub.forms import SharedRepoCreateForm
# Get an instance of a logger # Get an instance of a logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -469,7 +463,7 @@ def group_info(request, group):
"is_staff": group.is_staff, "is_staff": group.is_staff,
'create_shared_repo': True, 'create_shared_repo': True,
'group_members_default_display': GROUP_MEMBERS_DEFAULT_DISPLAY, 'group_members_default_display': GROUP_MEMBERS_DEFAULT_DISPLAY,
}, context_instance=RequestContext(request)); }, context_instance=RequestContext(request))
@group_check @group_check
def group_members(request, group): def group_members(request, group):
@@ -495,7 +489,7 @@ def group_members(request, group):
"members": members, "members": members,
"group" : group, "group" : group,
"is_staff": group.is_staff, "is_staff": group.is_staff,
}, context_instance=RequestContext(request)); }, context_instance=RequestContext(request))
@login_required @login_required
@group_staff_required @group_staff_required
@@ -1146,7 +1140,7 @@ def group_discuss(request, group):
"group_msgs": group_msgs, "group_msgs": group_msgs,
"form": form, "form": form,
'group_members_default_display': GROUP_MEMBERS_DEFAULT_DISPLAY, 'group_members_default_display': GROUP_MEMBERS_DEFAULT_DISPLAY,
}, context_instance=RequestContext(request)); }, context_instance=RequestContext(request))
class WikiDoesNotExist(Exception): class WikiDoesNotExist(Exception):
@@ -1166,25 +1160,24 @@ def find_wiki_repo(request, group):
except GroupWiki.DoesNotExist: except GroupWiki.DoesNotExist:
return None return None
def get_file_url(repo_id, path, filename): def get_file_url(repo, obj_id, file_name):
obj_id = get_file_id_by_path(repo_id, path) repo_id = repo.id
if not obj_id:
raise WikiPageMissing
access_token = seafserv_rpc.web_get_access_token(repo_id, obj_id, access_token = seafserv_rpc.web_get_access_token(repo_id, obj_id,
'view', '') 'view', '')
url = gen_file_get_url(access_token, filename) url = gen_file_get_url(access_token, file_name)
return url, obj_id return url
def get_wiki_page(request, group, page_name): def get_wiki_page(request, group, page_name):
repo = find_wiki_repo(request, group) repo = find_wiki_repo(request, group)
if not repo: if not repo:
raise WikiDoesNotExist raise WikiDoesNotExist
path = "/" + page_name + ".md" dirent = get_wiki_dirent(repo.id, page_name)
filename = page_name + ".md" if not dirent:
url, obj_id = get_file_url(repo.id, path, filename) raise WikiPageMissing
url = get_file_url(repo, dirent.obj_id, dirent.obj_name)
file_response = urllib2.urlopen(url) file_response = urllib2.urlopen(url)
content = file_response.read() content = file_response.read()
return content, repo.id, obj_id return content, repo.id, dirent
def convert_wiki_link(content, group, repo_id, username): def convert_wiki_link(content, group, repo_id, username):
import re import re
@@ -1193,28 +1186,27 @@ def convert_wiki_link(content, group, repo_id, username):
if matchobj.group(2): # return origin string in backquotes if matchobj.group(2): # return origin string in backquotes
return matchobj.group(2) return matchobj.group(2)
linkname = matchobj.group(1).strip() page_name = matchobj.group(1).strip()
filetype, fileext = get_file_type_and_ext(linkname) filetype, fileext = get_file_type_and_ext(page_name)
if fileext == '': if fileext == '':
# convert linkname that extension is missing to a markdown page # convert page_name that extension is missing to a markdown page
filename = linkname + ".md" dirent = get_wiki_dirent(repo_id, page_name)
path = "/" + filename if dirent is not None:
if get_file_id_by_path(repo_id, path):
a_tag = "<a href='%s'>%s</a>" a_tag = "<a href='%s'>%s</a>"
return a_tag % (reverse('group_wiki', args=[group.id, linkname]), linkname) return a_tag % (reverse('group_wiki', args=[group.id, normalize_page_name(page_name)]), page_name)
else: else:
a_tag = '''<a class="wiki-page-missing" href='%s'>%s</a>''' a_tag = '''<a class="wiki-page-missing" href='%s'>%s</a>'''
return a_tag % (reverse('group_wiki', args=[group.id, linkname.replace('/', '-')]), linkname) return a_tag % (reverse('group_wiki', args=[group.id, page_name.replace('/', '-')]), page_name)
elif filetype == IMAGE: elif filetype == IMAGE:
# load image to wiki page # load image to wiki page
path = "/" + linkname path = "/" + page_name
filename = os.path.basename(path) filename = os.path.basename(path)
obj_id = get_file_id_by_path(repo_id, path) obj_id = get_file_id_by_path(repo_id, path)
if not obj_id: if not obj_id:
# Replace '/' in linkname to '-', since wiki name can not # Replace '/' in page_name to '-', since wiki name can not
# contain '/'. # contain '/'.
return '''<a class="wiki-page-missing" href='%s'>%s</a>''' % \ return '''<a class="wiki-page-missing" href='%s'>%s</a>''' % \
(reverse('group_wiki', args=[group.id, linkname.replace('/', '-')]), linkname) (reverse('group_wiki', args=[group.id, page_name.replace('/', '-')]), page_name)
token = web_get_access_token(repo_id, obj_id, 'view', username) token = web_get_access_token(repo_id, obj_id, 'view', username)
return '<img src="%s" alt="%s" />' % (gen_file_get_url(token, filename), filename) return '<img src="%s" alt="%s" />' % (gen_file_get_url(token, filename), filename)
@@ -1222,12 +1214,12 @@ def convert_wiki_link(content, group, repo_id, username):
from base.templatetags.seahub_tags import file_icon_filter from base.templatetags.seahub_tags import file_icon_filter
# convert other types of filelinks to clickable links # convert other types of filelinks to clickable links
path = "/" + linkname path = "/" + page_name
icon = file_icon_filter(linkname) icon = file_icon_filter(page_name)
s = reverse('repo_view_file', args=[repo_id]) + \ s = reverse('repo_view_file', args=[repo_id]) + \
'?p=' + urllib2.quote(smart_str(path)) '?p=' + urllib2.quote(smart_str(path))
a_tag = '''<img src="%simg/file/%s" alt="%s" class="vam" /> <a href='%s' target='_blank' class="vam">%s</a>''' a_tag = '''<img src="%simg/file/%s" alt="%s" class="vam" /> <a href='%s' target='_blank' class="vam">%s</a>'''
return a_tag % (MEDIA_URL, icon, icon, s, linkname) return a_tag % (MEDIA_URL, icon, icon, s, page_name)
return re.sub(r'\[\[(.+)\]\]|(`.+`)', repl, content) return re.sub(r'\[\[(.+)\]\]|(`.+`)', repl, content)
@@ -1235,14 +1227,16 @@ def convert_wiki_link(content, group, repo_id, username):
@group_check @group_check
def group_wiki(request, group, page_name="home"): def group_wiki(request, group, page_name="home"):
username = request.user.username username = request.user.username
content = ''
wiki_exists = True wiki_exists = True
last_modified, latest_contributor = None, None
path, repo_id = '', ''
try: try:
content, repo_id, obj_id = get_wiki_page(request, group, page_name) content, repo_id, dirent = get_wiki_page(request, group, page_name)
except WikiDoesNotExist: except WikiDoesNotExist:
wiki_exists = False wiki_exists = False
return render_to_response("group/group_wiki.html", {
"group" : group,
"is_staff": group.is_staff,
"wiki_exists": wiki_exists,
}, context_instance=RequestContext(request))
except WikiPageMissing: except WikiPageMissing:
'''create that page for user if he/she is a group member''' '''create that page for user if he/she is a group member'''
if not is_group_user(group.id, username): if not is_group_user(group.id, username):
@@ -1251,7 +1245,7 @@ def group_wiki(request, group, page_name="home"):
repo = find_wiki_repo(request, group) repo = find_wiki_repo(request, group)
# No need to check whether repo is none, since repo is already created # No need to check whether repo is none, since repo is already created
filename = normalize_page_name(page_name) + '.md' filename = clean_page_name(page_name) + '.md'
if not post_empty_file(repo.id, "/", filename, username): if not post_empty_file(repo.id, "/", filename, username):
return render_error(request, _("Failed to create wiki page. Please retry later.")) return render_error(request, _("Failed to create wiki page. Please retry later."))
return HttpResponseRedirect(reverse('group_wiki', args=[group.id, page_name])) return HttpResponseRedirect(reverse('group_wiki', args=[group.id, page_name]))
@@ -1259,24 +1253,23 @@ def group_wiki(request, group, page_name="home"):
content = convert_wiki_link(content, group, repo_id, username) content = convert_wiki_link(content, group, repo_id, username)
# fetch file latest contributor and last modified # fetch file latest contributor and last modified
path = '/' + page_name + '.md' path = '/' + dirent.obj_name
file_path_hash = md5_constructor(urllib2.quote(path.encode('utf-8'))).hexdigest()[:12] file_path_hash = md5_constructor(urllib2.quote(path.encode('utf-8'))).hexdigest()[:12]
contributors, last_modified, last_commit_id = get_file_contributors(\ contributors, last_modified, last_commit_id = get_file_contributors(\
repo_id, path.encode('utf-8'), file_path_hash, obj_id) repo_id, path.encode('utf-8'), file_path_hash, dirent.obj_id)
latest_contributor = contributors[0] if contributors else None latest_contributor = contributors[0] if contributors else None
return render_to_response("group/group_wiki.html", { return render_to_response("group/group_wiki.html", {
"group" : group, "group" : group,
"is_staff": group.is_staff, "is_staff": group.is_staff,
"content": content, "wiki_exists": wiki_exists,
"page": page_name, "content": content,
"wiki_exists": wiki_exists, "page": os.path.splitext(dirent.obj_name)[0],
"last_modified": last_modified, "last_modified": last_modified,
"latest_contributor": latest_contributor, "latest_contributor": latest_contributor,
"path": path, "path": path,
"repo_id": repo_id, "repo_id": repo_id,
}, context_instance=RequestContext(request)) }, context_instance=RequestContext(request))
@group_check @group_check
def group_wiki_pages(request, group): def group_wiki_pages(request, group):
@@ -1299,13 +1292,14 @@ def group_wiki_pages(request, group):
except SearpcError, e: except SearpcError, e:
return render_error(request, _('Failed to list wiki directories.')) return render_error(request, _('Failed to list wiki directories.'))
pages = [] pages = {}
for e in dirs: for e in dirs:
if stat.S_ISDIR(e.mode): if stat.S_ISDIR(e.mode):
continue # skip directories continue # skip directories
name, ext = os.path.splitext(e.obj_name) name, ext = os.path.splitext(e.obj_name)
if ext == '.md': if ext == '.md':
pages.append(name) key = normalize_page_name(name)
pages[key] = name
return render_to_response("group/group_wiki_pages.html", { return render_to_response("group/group_wiki_pages.html", {
"group": group, "group": group,
@@ -1364,12 +1358,6 @@ def group_wiki_create(request, group):
next = reverse('group_wiki', args=[group.id]) next = reverse('group_wiki', args=[group.id])
return HttpResponse(json.dumps({'href': next}), content_type=content_type) return HttpResponse(json.dumps({'href': next}), content_type=content_type)
SLUG_OK = "!@#$%^&()_+-,.;'"
def normalize_page_name(page_name):
# Replace special characters to '-'.
# Do not lower page name and spaces are allowed.
return slugify(page_name, ok=SLUG_OK, lower=False, spaces=True)
@group_check @group_check
def group_wiki_page_new(request, group, page_name="home"): def group_wiki_page_new(request, group, page_name="home"):
if group.view_perm == "pub": if group.view_perm == "pub":
@@ -1381,7 +1369,7 @@ def group_wiki_page_new(request, group, page_name="home"):
page_name = request.POST.get('page_name', '') page_name = request.POST.get('page_name', '')
if not page_name: if not page_name:
return HttpResponseRedirect(request.META.get('HTTP_REFERER')) return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
page_name = normalize_page_name(page_name) # normalize page name page_name = clean_page_name(page_name)
repo = find_wiki_repo(request, group) repo = find_wiki_repo(request, group)
if not repo: if not repo:

10
utils/repo.py Normal file
View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
import seaserv
from seahub.utils import EMPTY_SHA1
def list_dir_by_path(cmmt, path):
if cmmt.root_id == EMPTY_SHA1:
return []
else:
return seaserv.list_dir_by_path(cmmt.id, path)

38
utils/wiki.py Normal file
View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
import stat
import seaserv
from seahub.utils import EMPTY_SHA1
from seahub.utils.repo import list_dir_by_path
from seahub.utils.slugify import slugify
SLUG_OK = "!@#$%^&()_+-,.;'"
def normalize_page_name(page_name):
# Remove special characters. Lower page name and replace spaces with '-'.
return slugify(page_name, ok=SLUG_OK)
def clean_page_name(page_name):
# Remove special characters. Do not lower page name and spaces are allowed.
return slugify(page_name, ok=SLUG_OK, lower=False, spaces=True)
def get_wiki_dirent(repo_id, page_name):
file_name = page_name + ".md"
repo = seaserv.get_repo(repo_id)
if not repo:
return None
cmmt = seaserv.get_commits(repo.id, 0, 1)[0]
if cmmt is None:
return None
dirs = list_dir_by_path(cmmt, "/")
if not dirs:
return None
else:
for e in dirs:
if stat.S_ISDIR(e.mode):
continue # skip directories
if normalize_page_name(file_name) == normalize_page_name(e.obj_name):
return e
return None

View File

@@ -39,6 +39,7 @@ from seahub.utils import get_httpserver_root, show_delete_days, render_error, \
is_textual_file, show_delete_days is_textual_file, show_delete_days
from seahub.utils.file_types import (IMAGE, PDF, IMAGE, DOCUMENT, MARKDOWN, \ from seahub.utils.file_types import (IMAGE, PDF, IMAGE, DOCUMENT, MARKDOWN, \
TEXT, SF) TEXT, SF)
from seahub.utils.wiki import get_wiki_dirent
from seahub.settings import FILE_ENCODING_LIST, FILE_PREVIEW_MAX_SIZE, \ from seahub.settings import FILE_ENCODING_LIST, FILE_PREVIEW_MAX_SIZE, \
FILE_ENCODING_TRY_LIST, USE_PDFJS, MEDIA_URL FILE_ENCODING_TRY_LIST, USE_PDFJS, MEDIA_URL
try: try:
@@ -220,11 +221,14 @@ def convert_md_link(file_content, repo_id, username):
filetype, fileext = get_file_type_and_ext(linkname) filetype, fileext = get_file_type_and_ext(linkname)
if fileext == '': if fileext == '':
# convert linkname that extension is missing to a markdown page # convert linkname that extension is missing to a markdown page
filename = linkname + ".md" dirent = get_wiki_dirent(repo_id, linkname)
path = "/" + filename
# filename = linkname + ".md"
path = "/" + dirent.obj_name
href = reverse('repo_view_file', args=[repo_id]) + '?p=' + path href = reverse('repo_view_file', args=[repo_id]) + '?p=' + path
if get_file_id_by_path(repo_id, path): if dirent is not None:
# if get_file_id_by_path(repo_id, path):
a_tag = '''<a href="%s">%s</a>''' a_tag = '''<a href="%s">%s</a>'''
return a_tag % (href, linkname) return a_tag % (href, linkname)
else: else: