mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-05 17:02:47 +00:00
Optimize wiki module (#5213)
* optimize code * optimize code * python load wiki file_content * optimize style * improve code * optimize code * update code * optimize wiki style * format <h> label * add scroll interactive * optimize code * repair code bug * format datetime Co-authored-by: 王健辉 <40563566+mrwangjianhui@users.noreply.github.com>
This commit is contained in:
@@ -105,7 +105,7 @@ class MainPanel extends Component {
|
|||||||
{this.props.permission == 'rw' && (
|
{this.props.permission == 'rw' && (
|
||||||
Utils.isDesktop() ?
|
Utils.isDesktop() ?
|
||||||
<button className="btn btn-secondary operation-item" title={gettext('Edit')} onClick={this.onEditClick}>{gettext('Edit')}</button> :
|
<button className="btn btn-secondary operation-item" title={gettext('Edit')} onClick={this.onEditClick}>{gettext('Edit')}</button> :
|
||||||
<span className="fa fa-pencil-alt mobile-toolbar-icon" title={gettext('Edit')} onClick={this.onEditClick} style={{'font-size': '1.1rem'}}></span>
|
<span className="fa fa-pencil-alt mobile-toolbar-icon" title={gettext('Edit')} onClick={this.onEditClick} style={{'fontSize': '1.1rem'}}></span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="common-toolbar">
|
<div className="common-toolbar">
|
||||||
|
@@ -27,7 +27,7 @@ class Wiki extends Component {
|
|||||||
pathExist: true,
|
pathExist: true,
|
||||||
closeSideBar: false,
|
closeSideBar: false,
|
||||||
isViewFile: true,
|
isViewFile: true,
|
||||||
isDataLoading: true,
|
isDataLoading: false,
|
||||||
direntList: [],
|
direntList: [],
|
||||||
content: '',
|
content: '',
|
||||||
permission: '',
|
permission: '',
|
||||||
@@ -43,6 +43,7 @@ class Wiki extends Component {
|
|||||||
window.onpopstate = this.onpopstate;
|
window.onpopstate = this.onpopstate;
|
||||||
this.indexPath = '/index.md';
|
this.indexPath = '/index.md';
|
||||||
this.homePath = '/home.md';
|
this.homePath = '/home.md';
|
||||||
|
this.pythonWrapper = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
@@ -52,39 +53,54 @@ class Wiki extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.loadWikiData(initialPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadWikiData = (initialPath) => {
|
|
||||||
this.loadSidePanel(initialPath);
|
this.loadSidePanel(initialPath);
|
||||||
|
this.loadWikiData(initialPath);
|
||||||
|
|
||||||
if (isDir === 'None') {
|
this.links = document.querySelectorAll(`#wiki-file-content a`);
|
||||||
if (initialPath === '/home.md') {
|
this.links.forEach(link => link.addEventListener('click', this.onConentLinkClick));
|
||||||
this.showDir('/');
|
|
||||||
} else {
|
|
||||||
this.setState({pathExist: false});
|
|
||||||
let fileUrl = siteRoot + 'published/' + slug + Utils.encodePath(initialPath);
|
|
||||||
window.history.pushState({url: fileUrl, path: initialPath}, initialPath, fileUrl);
|
|
||||||
}
|
|
||||||
} else if (isDir === 'True') {
|
|
||||||
this.showDir(initialPath);
|
|
||||||
} else if (isDir === 'False') {
|
|
||||||
this.showFile(initialPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.links.forEach(link => link.removeEventListener('click', this.onConentLinkClick));
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSidePanel = (initialPath) => {
|
loadSidePanel = (initialPath) => {
|
||||||
if (hasIndex) {
|
if (hasIndex) {
|
||||||
this.loadIndexNode();
|
this.loadIndexNode();
|
||||||
} else {
|
return;
|
||||||
if (isDir === 'None') {
|
|
||||||
initialPath = '/';
|
|
||||||
this.loadNodeAndParentsByPath('/');
|
|
||||||
} else {
|
|
||||||
this.loadNodeAndParentsByPath(initialPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load dir list
|
||||||
|
initialPath = isDir === 'None' ? '/' : initialPath;
|
||||||
|
this.loadNodeAndParentsByPath(initialPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadWikiData = (initialPath) => {
|
||||||
|
this.pythonWrapper = document.getElementById('wiki-file-content');
|
||||||
|
if (isDir === 'False') {
|
||||||
|
// this.showFile(initialPath);
|
||||||
|
this.setState({path: initialPath});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it is a file list, remove the template content provided by python
|
||||||
|
this.removePythonWrapper();
|
||||||
|
|
||||||
|
if (isDir === 'True') {
|
||||||
|
this.showDir(initialPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDir === 'None' && initialPath === '/home.md') {
|
||||||
|
this.showDir('/');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDir === 'None') {
|
||||||
|
this.setState({pathExist: false});
|
||||||
|
let fileUrl = siteRoot + 'published/' + slug + Utils.encodePath(initialPath);
|
||||||
|
window.history.pushState({url: fileUrl, path: initialPath}, initialPath, fileUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadIndexNode = () => {
|
loadIndexNode = () => {
|
||||||
@@ -120,6 +136,7 @@ class Wiki extends Component {
|
|||||||
path: filePath,
|
path: filePath,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.removePythonWrapper();
|
||||||
seafileAPI.getWikiFileContent(slug, filePath).then(res => {
|
seafileAPI.getWikiFileContent(slug, filePath).then(res => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -221,6 +238,29 @@ class Wiki extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removePythonWrapper = () => {
|
||||||
|
if (this.pythonWrapper) {
|
||||||
|
document.body.removeChild(this.pythonWrapper);
|
||||||
|
this.pythonWrapper = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onConentLinkClick = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
let link = '';
|
||||||
|
if (event.target.tagName !== 'A') {
|
||||||
|
let target = event.target.parentNode;
|
||||||
|
while (target.tagName !== 'A') {
|
||||||
|
target = target.parentNode;
|
||||||
|
}
|
||||||
|
link = target.href;
|
||||||
|
} else {
|
||||||
|
link = event.target.href;
|
||||||
|
}
|
||||||
|
this.onLinkClick(link);
|
||||||
|
}
|
||||||
|
|
||||||
onLinkClick = (link) => {
|
onLinkClick = (link) => {
|
||||||
const url = link;
|
const url = link;
|
||||||
if (Utils.isWikiInternalMarkdownLink(url, slug)) {
|
if (Utils.isWikiInternalMarkdownLink(url, slug)) {
|
||||||
|
@@ -1220,3 +1220,124 @@ a.table-sort-op:hover {
|
|||||||
box-shadow: 0 0 6px #ccc;
|
box-shadow: 0 0 6px #ccc;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#wiki-file-content {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 20%;
|
||||||
|
top: 90px;
|
||||||
|
z-index: 2;
|
||||||
|
background: #fff;
|
||||||
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .article {
|
||||||
|
margin-right: 200px;
|
||||||
|
padding: 10px 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline {
|
||||||
|
position: fixed;
|
||||||
|
top: 97px;
|
||||||
|
right: 0;
|
||||||
|
width: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#wiki-file-content {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .article {
|
||||||
|
margin-right: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.seafile-md-viewer-content .article {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.seafile-md-viewer-content {
|
||||||
|
background: #fff;
|
||||||
|
padding: 70px 75px;
|
||||||
|
border:1px solid #e6e6dd;
|
||||||
|
min-height: calc(100% - 60px);
|
||||||
|
}
|
||||||
|
.seafile-md-viewer-outline-heading2,
|
||||||
|
.seafile-md-viewer-outline-heading3 {
|
||||||
|
margin-left: .75rem;
|
||||||
|
line-height: 2.5;
|
||||||
|
color:#666;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow:hidden;
|
||||||
|
text-overflow:ellipsis;
|
||||||
|
cursor:pointer;
|
||||||
|
}
|
||||||
|
.seafile-md-viewer-outline-heading3 {
|
||||||
|
margin-left: 2rem;
|
||||||
|
}
|
||||||
|
.seafile-md-viewer-outline-heading2:hover,
|
||||||
|
.seafile-md-viewer-outline-heading3:hover {
|
||||||
|
color: #eb8205;
|
||||||
|
}
|
||||||
|
.seafile-markdown-outline {
|
||||||
|
position: fixed;
|
||||||
|
padding-right: 1rem;
|
||||||
|
top: 97px;
|
||||||
|
right: 0;
|
||||||
|
width: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
.seafile-editor-outline {
|
||||||
|
border-left: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
.seafile-markdown-outline .active {
|
||||||
|
color: #eb8205;
|
||||||
|
border-left: 1px solid #eb8205;
|
||||||
|
}
|
||||||
|
.seafile-markdown-outline .outline-h2,
|
||||||
|
.seafile-markdown-outline .outline-h3 {
|
||||||
|
height: 30px;
|
||||||
|
margin-left: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.seafile-markdown-outline .outline-h2 {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.seafile-markdown-outline .outline-h3 {
|
||||||
|
padding-left: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline .outline-h2,
|
||||||
|
#wiki-file-content .seafile-markdown-outline .outline-h3 {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4d5156;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline .outline-h2.active,
|
||||||
|
#wiki-file-content .seafile-markdown-outline .outline-h3.active {
|
||||||
|
color: #eb8205;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline .seafile-markdown-outline {
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki-file-content .seafile-markdown-outline .seafile-markdown-outline:hover {
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -22,3 +22,4 @@ python-cas
|
|||||||
djangosaml2==0.20.0
|
djangosaml2==0.20.0
|
||||||
pysaml2==6.5.1
|
pysaml2==6.5.1
|
||||||
cffi==1.14.0
|
cffi==1.14.0
|
||||||
|
Markdown
|
||||||
|
@@ -304,6 +304,8 @@ def translate_seahub_time(value, autoescape=None):
|
|||||||
|
|
||||||
return mark_safe(time_with_tag)
|
return mark_safe(time_with_tag)
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter(name='translate_seahub_time_str')
|
||||||
def translate_seahub_time_str(val):
|
def translate_seahub_time_str(val):
|
||||||
"""Convert python datetime to human friendly format."""
|
"""Convert python datetime to human friendly format."""
|
||||||
|
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="wrapper" class="{{ LANGUAGE_CODE }}"></div>
|
<div id="wrapper" class="{{ LANGUAGE_CODE }}"></div>
|
||||||
<div id="modal-wrapper" class="{{ LANGUAGE_CODE }}"></div>
|
<div id="modal-wrapper" class="{{ LANGUAGE_CODE }}"></div>
|
||||||
|
{%block extra_content %}{% endblock %}
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.app = {
|
window.app = {
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base_for_react.html" %}
|
{% extends "base_for_react.html" %}
|
||||||
|
{% load i18n %}
|
||||||
{% load render_bundle from webpack_loader %}
|
{% load render_bundle from webpack_loader %}
|
||||||
{% load seahub_tags %}
|
{% load seahub_tags %}
|
||||||
{% block extra_ogp_tags %}
|
{% block extra_ogp_tags %}
|
||||||
@@ -16,6 +17,24 @@
|
|||||||
|
|
||||||
{% block sub_title %} {{filename}} - {% endblock %}
|
{% block sub_title %} {{filename}} - {% endblock %}
|
||||||
|
|
||||||
|
{% block extra_content %}
|
||||||
|
{% if not is_dir %}
|
||||||
|
<div id="wiki-file-content" class="{{ LANGUAGE_CODE }}">
|
||||||
|
<div class="article">
|
||||||
|
{{ file_content }}
|
||||||
|
<p id="wiki-page-last-modified">{% translate "Last modified by" %} {{modifier|email2nickname}}, <span>{{modify_time|translate_seahub_time_str}}</span></p>
|
||||||
|
</div>
|
||||||
|
<div class="seafile-markdown-outline">
|
||||||
|
<div id="seafile-editor-outline" class="seafile-editor-outline">
|
||||||
|
{% for outline in outlines %}
|
||||||
|
{{ outline }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block extra_script %}
|
{% block extra_script %}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.wiki = {
|
window.wiki = {
|
||||||
@@ -32,6 +51,71 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
// titles info
|
||||||
|
let titlesInfo = [];
|
||||||
|
let headingList = document.querySelectorAll('h2[id^="user-content"], h3[id^="user-content"]');
|
||||||
|
for (let i = 0; i < headingList.length; i++) {
|
||||||
|
titlesInfo.push(headingList[i].offsetTop);
|
||||||
|
}
|
||||||
|
|
||||||
|
// outline infos
|
||||||
|
const outlineInfos = document.querySelectorAll('.outline-h2, .outline-h3');
|
||||||
|
const addActiveClass = (item) => {
|
||||||
|
const className = item.className;
|
||||||
|
if (className.indexOf('active') > -1) return;
|
||||||
|
item.className += ' active';
|
||||||
|
};
|
||||||
|
const removeActiveClass = (item) => {
|
||||||
|
const className = item.className;
|
||||||
|
if (className.indexOf('active') === -1) return;
|
||||||
|
item.className = className.replace(/(?:^|\s)active(?!\S)/g, '');
|
||||||
|
};
|
||||||
|
const updateOutline = (activeIndex) => {
|
||||||
|
for (let i = 0; i < outlineInfos.length; i++) {
|
||||||
|
const item = outlineInfos[i];
|
||||||
|
if (activeIndex !== i) {
|
||||||
|
removeActiveClass(item);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
addActiveClass(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const outlineContainer = document.getElementById('seafile-editor-outline');
|
||||||
|
outlineContainer && outlineContainer.addEventListener('click', (event) => {
|
||||||
|
const text = event.target.innerText;
|
||||||
|
let url = new URL(window.location.href);
|
||||||
|
url.hash = '#user-content-' + text;
|
||||||
|
window.location.href = url.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
// scroll event handle
|
||||||
|
const container = document.getElementById('wiki-file-content');
|
||||||
|
container && container.addEventListener('scroll', () => {
|
||||||
|
const titlesLength = titlesInfo.length;
|
||||||
|
const contentScrollTop = container.scrollTop + 180;
|
||||||
|
let activeTitleIndex;
|
||||||
|
if (contentScrollTop <= titlesInfo[0]) {
|
||||||
|
activeTitleIndex = 0;
|
||||||
|
} else if (contentScrollTop > titlesInfo[titlesLength - 1]) {
|
||||||
|
activeTitleIndex = titlesInfo.length - 1;
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < titlesLength; i++) {
|
||||||
|
if (contentScrollTop > titlesInfo[i]) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
activeTitleIndex = i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateOutline(activeTitleIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
// set first outline to active
|
||||||
|
updateOutline(0);
|
||||||
|
</script>
|
||||||
|
|
||||||
{% render_bundle 'wiki' 'js' %}
|
{% render_bundle 'wiki' 'js' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@@ -1,22 +1,28 @@
|
|||||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import logging
|
import logging
|
||||||
import urllib.request, urllib.error, urllib.parse
|
import urllib.request
|
||||||
import posixpath
|
import posixpath
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import seaserv
|
import markdown
|
||||||
|
import lxml.html
|
||||||
from seaserv import seafile_api
|
from seaserv import seafile_api
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.http import Http404, HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from seahub.auth.decorators import login_required
|
|
||||||
from seahub.share.models import FileShare
|
from seahub.share.models import FileShare
|
||||||
from seahub.wiki.models import Wiki
|
from seahub.wiki.models import Wiki
|
||||||
from seahub.views import check_folder_permission
|
from seahub.views import check_folder_permission
|
||||||
from seahub.utils import get_service_url, get_file_type_and_ext, render_permission_error
|
from seahub.utils import get_file_type_and_ext, render_permission_error, \
|
||||||
|
gen_inner_file_get_url, render_error
|
||||||
|
from seahub.views.file import send_file_access_msg
|
||||||
from seahub.utils.file_types import *
|
from seahub.utils.file_types import *
|
||||||
|
from seahub.settings import SERVICE_URL
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -88,6 +94,98 @@ def slug(request, slug, file_path="home.md"):
|
|||||||
|
|
||||||
repo = seafile_api.get_repo(wiki.repo_id)
|
repo = seafile_api.get_repo(wiki.repo_id)
|
||||||
|
|
||||||
|
file_content, outlines, latest_contributor, last_modified = '', [], '', 0
|
||||||
|
if is_dir is False:
|
||||||
|
send_file_access_msg(request, repo, file_path, 'web')
|
||||||
|
|
||||||
|
file_name = os.path.basename(file_path)
|
||||||
|
token = seafile_api.get_fileserver_access_token(
|
||||||
|
repo.repo_id, file_id, 'download', request.user.username, 'False')
|
||||||
|
if not token:
|
||||||
|
return render_error(request, _('Internal Server Error'))
|
||||||
|
|
||||||
|
url = gen_inner_file_get_url(token, file_name)
|
||||||
|
try:
|
||||||
|
file_response = urllib.request.urlopen(url).read().decode()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return render_error(request, _('Internal Server Error'))
|
||||||
|
|
||||||
|
if file_type == MARKDOWN:
|
||||||
|
# Convert a markdown string to HTML
|
||||||
|
try:
|
||||||
|
html_content = markdown.markdown(file_response)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return render_error(request, _('Internal Server Error'))
|
||||||
|
|
||||||
|
# Parse the html and replace image url to wiki mode
|
||||||
|
html_doc = lxml.html.fromstring(html_content)
|
||||||
|
img_elements = html_doc.xpath('//img') # Get the <img> elements
|
||||||
|
img_url_re = re.compile(r'^%s/lib/%s/file/.*raw=1$' % (SERVICE_URL.strip('/'), repo.id))
|
||||||
|
for img in img_elements:
|
||||||
|
img_url = img.attrib.get('src', '')
|
||||||
|
if img_url_re.match(img_url) is not None:
|
||||||
|
img_path = img_url[img_url.find('/file/')+5:img_url.find('?')]
|
||||||
|
new_img_url = '%s/view-image-via-public-wiki/?slug=%s&path=%s' \
|
||||||
|
% (SERVICE_URL.strip('/'), slug, img_path)
|
||||||
|
html_content = html_content.replace(img_url, new_img_url)
|
||||||
|
elif re.compile(r'^\.\./*|^\./').match(img_url):
|
||||||
|
if img_url.startswith('../'):
|
||||||
|
img_path = os.path.join(os.path.dirname(os.path.dirname(file_path)), img_url[3:])
|
||||||
|
else:
|
||||||
|
img_path = os.path.join(os.path.dirname(file_path), img_url[2:])
|
||||||
|
new_img_url = '%s/view-image-via-public-wiki/?slug=%s&path=%s' \
|
||||||
|
% (SERVICE_URL.strip('/'), slug, img_path)
|
||||||
|
html_content = html_content.replace(img_url, new_img_url)
|
||||||
|
|
||||||
|
# Replace link url to wiki mode
|
||||||
|
link_elements = html_doc.xpath('//a') # Get the <a> elements
|
||||||
|
file_link_re = re.compile(r'^%s/lib/%s/file/.*' % (SERVICE_URL.strip('/'), repo.id))
|
||||||
|
md_link_re = re.compile(r'^%s/lib/%s/file/.*\.md$' % (SERVICE_URL.strip('/'), repo.id))
|
||||||
|
dir_link_re = re.compile(r'^%s/library/%s/(.*)' % (SERVICE_URL.strip('/'), repo.id))
|
||||||
|
for link in link_elements:
|
||||||
|
link_url = link.attrib.get('href', '')
|
||||||
|
if file_link_re.match(link_url) is not None:
|
||||||
|
link_path = link_url[link_url.find('/file/') + 5:].strip('/')
|
||||||
|
if md_link_re.match(link_url) is not None:
|
||||||
|
new_md_url = '%s/published/%s/%s' % (SERVICE_URL.strip('/'), slug, link_path)
|
||||||
|
html_content = html_content.replace(link_url, new_md_url)
|
||||||
|
else:
|
||||||
|
new_file_url = '%s/d/%s/files/?p=%s&dl=1' % (SERVICE_URL.strip('/'), fs.token, link_path)
|
||||||
|
html_content = html_content.replace(link_url, new_file_url)
|
||||||
|
elif dir_link_re.match(link_url) is not None:
|
||||||
|
link_path = dir_link_re.match(link_url).groups()[0].strip('/')
|
||||||
|
dir_path = link_path[link_path.find('/'):].strip('/')
|
||||||
|
new_dir_url = '%s/published/%s/%s' % (SERVICE_URL.strip('/'), slug, dir_path)
|
||||||
|
html_content = html_content.replace(link_url, new_dir_url)
|
||||||
|
|
||||||
|
# Get markdown outlines and format <h> label
|
||||||
|
for p in html_content.split('\n'):
|
||||||
|
if p.startswith('<h1>') and p.endswith('</h1>'):
|
||||||
|
head = p.replace('<h1>', '<h1 id="user-content-%s">' % p.strip('<h1></h1>'), 1)
|
||||||
|
html_content = html_content.replace(p, head)
|
||||||
|
elif p.startswith('<h2>') and p.endswith('</h2>'):
|
||||||
|
head = p.replace('<h2>', '<h2 id="user-content-%s">' % p.strip('<h2></h2>'), 1)
|
||||||
|
html_content = html_content.replace(p, head)
|
||||||
|
outline = '<div class="outline-h2">' + p.strip('<h2></h2>') + '</div>'
|
||||||
|
outlines.append(mark_safe(outline))
|
||||||
|
elif p.startswith('<h3>') and p.endswith('</h3>'):
|
||||||
|
head = p.replace('<h3>', '<h3 id="user-content-%s">' % p.strip('<h3></h3>'), 1)
|
||||||
|
html_content = html_content.replace(p, head)
|
||||||
|
outline = '<div class="outline-h3">' + p.strip('<h3></h3>') + '</div>'
|
||||||
|
outlines.append(mark_safe(outline))
|
||||||
|
|
||||||
|
file_content = mark_safe(html_content)
|
||||||
|
|
||||||
|
try:
|
||||||
|
dirent = seafile_api.get_dirent_by_path(wiki.repo_id, file_path)
|
||||||
|
if dirent:
|
||||||
|
latest_contributor, last_modified = dirent.modifier, dirent.mtime
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(e)
|
||||||
|
last_modified = datetime.fromtimestamp(last_modified)
|
||||||
|
|
||||||
return render(request, "wiki/wiki.html", {
|
return render(request, "wiki/wiki.html", {
|
||||||
"wiki": wiki,
|
"wiki": wiki,
|
||||||
"repo_name": repo.name if repo else '',
|
"repo_name": repo.name if repo else '',
|
||||||
@@ -97,6 +195,10 @@ def slug(request, slug, file_path="home.md"):
|
|||||||
"user_can_write": user_can_write,
|
"user_can_write": user_can_write,
|
||||||
"file_path": file_path,
|
"file_path": file_path,
|
||||||
"filename": os.path.splitext(os.path.basename(file_path))[0],
|
"filename": os.path.splitext(os.path.basename(file_path))[0],
|
||||||
|
"file_content": file_content,
|
||||||
|
"outlines": outlines,
|
||||||
|
"modifier": latest_contributor,
|
||||||
|
"modify_time": last_modified,
|
||||||
"repo_id": wiki.repo_id,
|
"repo_id": wiki.repo_id,
|
||||||
"search_repo_id": wiki.repo_id,
|
"search_repo_id": wiki.repo_id,
|
||||||
"search_wiki": True,
|
"search_wiki": True,
|
||||||
|
Reference in New Issue
Block a user