Update application
This commit is contained in:
@@ -51,6 +51,9 @@ from . import ns_login
|
||||
# Профиль
|
||||
from . import ns_profile
|
||||
|
||||
# Метки
|
||||
from . import ns_tag
|
||||
|
||||
# Пользователи
|
||||
from . import ns_user
|
||||
|
||||
|
||||
@@ -24,6 +24,12 @@ Base.query = db_session.query_property()
|
||||
# Пользователи
|
||||
from .users import User
|
||||
|
||||
# Метки
|
||||
from .tag import Tag
|
||||
|
||||
# Статьи
|
||||
from .page import Page, TagPage
|
||||
|
||||
Base.metadata.create_all(engine)
|
||||
|
||||
__all__ = []
|
||||
|
||||
74
myapp/models/page.py
Normal file
74
myapp/models/page.py
Normal file
@@ -0,0 +1,74 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'http://remizoffalex.ru'
|
||||
|
||||
import datetime
|
||||
from sqlalchemy import Column, Boolean, Integer, ForeignKey, String, DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from . import Base
|
||||
|
||||
|
||||
class Page(Base):
|
||||
"""
|
||||
Страницы
|
||||
"""
|
||||
__tablename__ = "page"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
user_id = Column(Integer, ForeignKey('user.id'))
|
||||
title = Column(String)
|
||||
body = Column(String, default='')
|
||||
created = Column(DateTime) # Дата создания
|
||||
updated = Column(DateTime) # Дата обновления
|
||||
|
||||
# Связи
|
||||
user = relationship(
|
||||
"User",
|
||||
primaryjoin="Page.user_id==User.id",
|
||||
uselist=False
|
||||
)
|
||||
tags = relationship("TagPage", primaryjoin="Page.id==TagPage.page_id")
|
||||
|
||||
|
||||
def __init__(self, user, title):
|
||||
assert type(user).__name__=='User', app.logger.info('Не передан объект User')
|
||||
self.user_id = user.id
|
||||
self.title = title
|
||||
self.created = datetime.datetime.now()
|
||||
self.updated = datetime.datetime.now()
|
||||
|
||||
def __repr__(self):
|
||||
return "<Page('{}')>".format(self.title)
|
||||
|
||||
def as_dict(self):
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
|
||||
|
||||
class TagPage(Base):
|
||||
"""Теги к страницам"""
|
||||
__tablename__ = "tagpage"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
page_id = Column(Integer, ForeignKey('page.id'))
|
||||
tag_id = Column(Integer, ForeignKey('tag.id'))
|
||||
created = Column(DateTime) # Дата создания
|
||||
|
||||
# Связи
|
||||
page = relationship("Page", primaryjoin="TagPage.page_id==Page.id", uselist=False)
|
||||
tag = relationship("Tag", primaryjoin="TagPage.tag_id==Tag.id", uselist=False)
|
||||
|
||||
def __init__(self, page, tag):
|
||||
assert type(page).__name__=='Page', app.logger.info('Не передан объект Page')
|
||||
assert type(tag).__name__=='Tag', app.logger.info('Не передан объект Tag')
|
||||
self.page_id = page.id
|
||||
self.tag_id = tag.id
|
||||
self.created = datetime.datetime.now()
|
||||
|
||||
def __repr__(self):
|
||||
return "<TagPage('%s')>" % (self.id)
|
||||
|
||||
def as_dict(self):
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
43
myapp/models/tag.py
Normal file
43
myapp/models/tag.py
Normal file
@@ -0,0 +1,43 @@
|
||||
"""
|
||||
Таблицы:
|
||||
Tag
|
||||
TagLogo
|
||||
"""
|
||||
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'http://remizoffalex.ru'
|
||||
|
||||
import datetime
|
||||
from sqlalchemy import Column, Integer, ForeignKey, String, DateTime
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from . import Base
|
||||
from .. import app
|
||||
|
||||
|
||||
class Tag(Base):
|
||||
"""
|
||||
Теги, метки
|
||||
"""
|
||||
__tablename__ = "tag"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String, nullable=False, unique=True)
|
||||
|
||||
# Связи
|
||||
pages = relationship("TagPage", primaryjoin="Tag.id==TagPage.tag_id")
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __repr__(self):
|
||||
return "<Tag('%s')>" % (self.name)
|
||||
|
||||
def as_dict(self):
|
||||
"""
|
||||
Возвращает словарь
|
||||
"""
|
||||
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
@@ -4,10 +4,35 @@ __license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from functools import wraps
|
||||
from flask_jsonrpc import JSONRPC
|
||||
from flask import session
|
||||
|
||||
from .. import app
|
||||
from .. import app, models
|
||||
|
||||
def login_required(func):
|
||||
@wraps(func)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'logged_in' in session and 'user_id' in session:
|
||||
user = models.db_session.query(
|
||||
models.User
|
||||
).filter(
|
||||
models.User.id==session['user_id']
|
||||
).first()
|
||||
if user:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
session.pop('logged_in', None)
|
||||
session.pop('user_id', None)
|
||||
raise Exception('Необходима авторизация')
|
||||
else:
|
||||
raise Exception('Необходима авторизация')
|
||||
return decorated_function
|
||||
|
||||
jsonrpc = JSONRPC(app, '/api')
|
||||
|
||||
from . import user
|
||||
from . import (
|
||||
page,
|
||||
tag,
|
||||
user
|
||||
)
|
||||
|
||||
21
myapp/ns_api/page.py
Normal file
21
myapp/ns_api/page.py
Normal file
@@ -0,0 +1,21 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from . import jsonrpc, login_required
|
||||
from .. import models
|
||||
|
||||
|
||||
@jsonrpc.method('page.update(title=str, text=str)')
|
||||
@login_required
|
||||
def page_update(title, text):
|
||||
"""
|
||||
Обновить статью
|
||||
"""
|
||||
result = {
|
||||
"title": title,
|
||||
"text": text
|
||||
}
|
||||
return result
|
||||
28
myapp/ns_api/tag.py
Normal file
28
myapp/ns_api/tag.py
Normal file
@@ -0,0 +1,28 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from . import jsonrpc, login_required
|
||||
from .. import models
|
||||
|
||||
|
||||
@jsonrpc.method('tag.add(name=str)')
|
||||
@login_required
|
||||
def tag_add(name):
|
||||
"""
|
||||
Добавить новый тег
|
||||
"""
|
||||
exist = models.db_session.query(
|
||||
models.Tag
|
||||
).filter(
|
||||
models.Tag.name == name
|
||||
).first()
|
||||
if exist:
|
||||
raise ValueError
|
||||
|
||||
newTag = models.Tag(name)
|
||||
models.db_session.add(newTag)
|
||||
models.db_session.commit()
|
||||
return newTag.as_dict()
|
||||
@@ -4,14 +4,12 @@ __license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from flask import abort, escape
|
||||
|
||||
from . import jsonrpc
|
||||
from .. import models
|
||||
|
||||
|
||||
@jsonrpc.method('users.getList')
|
||||
def users_getList():
|
||||
@jsonrpc.method('users')
|
||||
def users_list():
|
||||
"""
|
||||
Показать список пользователей
|
||||
"""
|
||||
|
||||
18
myapp/ns_tag/__init__.py
Normal file
18
myapp/ns_tag/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
import os
|
||||
import jinja2
|
||||
|
||||
from . import views
|
||||
from .. import app
|
||||
|
||||
|
||||
my_loader = jinja2.ChoiceLoader([
|
||||
app.jinja_loader,
|
||||
jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__)) + "/templates"),
|
||||
])
|
||||
app.jinja_loader = my_loader
|
||||
37
myapp/ns_tag/templates/tag.html
Normal file
37
myapp/ns_tag/templates/tag.html
Normal file
@@ -0,0 +1,37 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block body %}
|
||||
|
||||
{% raw %}
|
||||
<h3>
|
||||
<a class="btn btn-outline-secondary" :href="'/tag/' + tag.id"><i class="fa fa-chevron-left"></i></a>
|
||||
Тег {{ tag.name }}</h3>
|
||||
<hr />
|
||||
{% endraw %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{% raw %}
|
||||
<ol class="breadcrumb mt-3">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="fa fa-home"></i></a></li>
|
||||
<li class="breadcrumb-item"><a href="/tags">Список тегов</a></li>
|
||||
<li class="breadcrumb-item">Тег {{ tag.id }}</li>
|
||||
</ol>
|
||||
{% endraw %}
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript" src="/static/components/backtotop.js"></script>
|
||||
<link rel="stylesheet" href="/static/components/backtotop.css"></link>
|
||||
|
||||
<script type="text/javascript">
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
tag: {{ pagedata['tag']|tojson|safe }}
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
80
myapp/ns_tag/templates/tags.html
Normal file
80
myapp/ns_tag/templates/tags.html
Normal file
@@ -0,0 +1,80 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block body %}
|
||||
|
||||
<h3>
|
||||
<div class="btn btn-outline-success btn-sm float-right" v-on:click="showFormNewTag"><i class="fa fa-plus"></i></div>
|
||||
Список тегов</h3>
|
||||
<hr />
|
||||
|
||||
{% raw %}
|
||||
<div class="form-group mt-3" v-if="formNewTag">
|
||||
<div class="input-group mb-3">
|
||||
<input class="form-control" type="text" v-model="newTag"/>
|
||||
<div class="input-group-append">
|
||||
<div class="btn btn-outline-success float-right" v-on:click="addTag"><i class="fa fa-save"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col py-2">
|
||||
<a class="btn btn-outline-secondary mr-2 mb-3" v-for="(tag, tagIdx) in tags" :href="'/tag/' + tag.id">{{ tag.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endraw %}
|
||||
|
||||
<backtotop-component></backtotop-component>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
<ol class="breadcrumb mt-3">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="fa fa-home"></i></a></li>
|
||||
<li class="breadcrumb-item">Список тегов</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript" src="/static/components/backtotop.js"></script>
|
||||
<link rel="stylesheet" href="/static/components/backtotop.css"></link>
|
||||
|
||||
<script type="text/javascript">
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
tags: {{ pagedata['tags']|tojson|safe }},
|
||||
formNewTag: false,
|
||||
newTag: ''
|
||||
},
|
||||
methods: {
|
||||
addTag: function() {
|
||||
/* Добавить тег */
|
||||
var vm = this;
|
||||
axios.post(
|
||||
'/api',
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": 'tag.add',
|
||||
"params": {
|
||||
"name": vm.newTag
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
).then(
|
||||
function(response) {
|
||||
if ('result' in response.data) {
|
||||
vm.tags.push(response.data['result']);
|
||||
vm.newTag = '';
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
showFormNewTag: function() {
|
||||
/* Показать/скрыть форму добавления нового тега */
|
||||
var vm = this;
|
||||
vm.formNewTag = !vm.formNewTag;
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
54
myapp/ns_tag/views.py
Normal file
54
myapp/ns_tag/views.py
Normal file
@@ -0,0 +1,54 @@
|
||||
__author__ = 'RemiZOffAlex'
|
||||
__copyright__ = '(c) RemiZOffAlex'
|
||||
__license__ = 'MIT'
|
||||
__email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from flask import render_template
|
||||
|
||||
from .. import app, models
|
||||
|
||||
|
||||
@app.route('/tags')
|
||||
def tags():
|
||||
"""
|
||||
Список меток
|
||||
"""
|
||||
pagedata = {}
|
||||
pagedata['title'] = 'Список меток - ' + app.config['TITLE']
|
||||
tags = models.db_session.query(
|
||||
models.Tag
|
||||
).order_by(
|
||||
models.Tag.name.asc()
|
||||
).all()
|
||||
|
||||
pagedata['tags'] = []
|
||||
for tag in tags:
|
||||
pagedata['tags'].append(tag.as_dict())
|
||||
|
||||
body = render_template('tags.html', pagedata=pagedata)
|
||||
return body
|
||||
|
||||
|
||||
@app.route('/tag/<int:id>')
|
||||
def tag_id(id):
|
||||
"""
|
||||
Метка
|
||||
"""
|
||||
pagedata = {}
|
||||
tag = models.db_session.query(
|
||||
models.Tag
|
||||
).filter(
|
||||
models.Tag.id == id
|
||||
).first()
|
||||
if tag is None:
|
||||
abort(404)
|
||||
|
||||
pagedata['title'] = 'Метка {} - {}'.format(
|
||||
tag.name,
|
||||
app.config['TITLE']
|
||||
)
|
||||
pagedata['tag'] = tag.as_dict()
|
||||
|
||||
body = render_template('tag.html', pagedata=pagedata)
|
||||
return body
|
||||
@@ -4,8 +4,6 @@
|
||||
<h3>Список пользователей</h3>
|
||||
<hr />
|
||||
|
||||
<div class="btn btn-outline-primary" v-on:click="getUserList()">Получить/обновить список пользователей</div>
|
||||
|
||||
{% raw %}
|
||||
<div class="row" v-for="(user, userIdx) in users">
|
||||
<div class="col py-2">
|
||||
@@ -48,7 +46,7 @@ var app = new Vue({
|
||||
'/api',
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": 'users.getList',
|
||||
"method": 'users',
|
||||
"params": {
|
||||
},
|
||||
"id": 1
|
||||
@@ -61,6 +59,10 @@ var app = new Vue({
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
var vm = this;
|
||||
vm.getUserList();
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
BIN
myapp/static/403.jpg
Normal file
BIN
myapp/static/403.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
myapp/static/404.jpg
Normal file
BIN
myapp/static/404.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
BIN
myapp/static/500.jpg
Normal file
BIN
myapp/static/500.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 171 KiB |
7
myapp/static/js/bootstrap.min.js
vendored
7
myapp/static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
2
myapp/static/js/jquery-3.3.1.slim.min.js
vendored
2
myapp/static/js/jquery-3.3.1.slim.min.js
vendored
File diff suppressed because one or more lines are too long
5
myapp/static/js/popper.min.js
vendored
5
myapp/static/js/popper.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,40 +0,0 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block body %}
|
||||
|
||||
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
|
||||
|
||||
<form method="post" action="/edit">
|
||||
|
||||
<div class="form-group">
|
||||
{{ pagedata['form'].title.label }}
|
||||
{{ pagedata['form'].title(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ pagedata['form'].text.label }}
|
||||
{{ pagedata['form'].text(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-success float-right"><i class="fa fa-save-o"></i> Сохранить</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
mounted: function () {
|
||||
CKEDITOR.replace( 'text', {
|
||||
customConfig: '/static/js/ckeditor-conf.js'
|
||||
} );
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
10
myapp/templates/error.html
Normal file
10
myapp/templates/error.html
Normal file
@@ -0,0 +1,10 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block body %}
|
||||
|
||||
<h3 class="text-danger">{{ error_code }}: {{ error_message }}</h3>
|
||||
<hr />
|
||||
|
||||
<h4 class="text-center">Это фиаско, братан!</h4>
|
||||
<p class="text-center"><img class="img-thumbnail" src="/static/{{ error_code }}.jpg" /></p>
|
||||
|
||||
{% endblock %}
|
||||
7
myapp/templates/footer.html
Normal file
7
myapp/templates/footer.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<div class="row mt-3 py-3 bg-light">
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-10">
|
||||
© <a href="https://remizoffalex.ru/" target="_blank">RemiZOffAlex</a>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
</div>
|
||||
@@ -5,9 +5,6 @@
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
<link rel="stylesheet" href="/static/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/static/css/font-awesome.min.css" />
|
||||
<script type="text/javascript" src="/static/js/jquery-3.3.1.slim.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/vue.min.js"></script>
|
||||
<script type="text/javascript" src="/static/js/axios.min.js"></script>
|
||||
<title>{{ pagedata['title'] }}</title>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<div class="container-fluid py-2">
|
||||
<div class="row">
|
||||
<div class="col py-2">
|
||||
|
||||
{% if session.logged_in %}
|
||||
<div class="btn-group float-right">
|
||||
@@ -10,7 +11,9 @@
|
||||
{% endif %}
|
||||
|
||||
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/edit"><i class="fa fa-edit"></i> Редактор</a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/users">Vue & axios</a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/page">Статья</a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
|
||||
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,112 @@
|
||||
{% extends "skeleton.html" %}
|
||||
{% block body %}
|
||||
|
||||
<h3>{{ pagedata['title'] }}</h3>
|
||||
{% raw %}
|
||||
<h3>
|
||||
<div class="btn btn-outline-secondary float-right" v-on:click="showPanel(panels.edit)"><i class="fa fa-edit"></i></div>
|
||||
{{ page.title }}</h3>
|
||||
<hr />
|
||||
|
||||
{{ pagedata['text']|safe }}
|
||||
<div class="row" v-if="panels.edit.visible">
|
||||
<div class="col">
|
||||
|
||||
{% endblock body %}
|
||||
<div class="form-group">
|
||||
<label for="title">Заголовок</label>
|
||||
<input id="title" name="title" type="text" value="Заголовок страницы" class="form-control" v-model="newPage.title">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="text">Текст</label>
|
||||
<textarea class="form-control" id="text" name="text" v-model="newPage.text"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button class="btn btn-outline-success float-right" v-on:click="send"><i class="fa fa-save-o"></i> Сохранить</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3" v-if="error">
|
||||
<div class="col bg-danger text-white" v-html="error">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<span v-html="page.text" v-else></span>
|
||||
|
||||
{% endraw %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
<ol class="breadcrumb mt-3">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="fa fa-home"></i></a></li>
|
||||
<li class="breadcrumb-item active">Редактирование страницы</li>
|
||||
</ol>
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
page: {{ pagedata['page']|tojson|safe }},
|
||||
newPage: {{ pagedata['page']|tojson|safe }},
|
||||
panels: {
|
||||
edit: {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
error: null
|
||||
},
|
||||
methods: {
|
||||
showPanel: function(panel) {
|
||||
/* Показать/скрыть панель */
|
||||
panel.visible = !panel.visible;
|
||||
},
|
||||
send: function() {
|
||||
var vm = this;
|
||||
var value = CKEDITOR.instances["text"].getData();
|
||||
if (value != vm.newPage.text) {
|
||||
vm.newPage.text = value;
|
||||
}
|
||||
axios.post(
|
||||
'/api',
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": 'page.update',
|
||||
"params": {
|
||||
"title": vm.newPage.title,
|
||||
"text": vm.newPage.text
|
||||
},
|
||||
"id": 1
|
||||
}
|
||||
).then(
|
||||
function(response) {
|
||||
if ('result' in response.data) {
|
||||
vm.page = response.data['result'];
|
||||
vm.newPage = vm.page;
|
||||
vm.error = null;
|
||||
} else if ('error' in response.data) {
|
||||
vm.error = response.data['error'].message;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
updated: function() {
|
||||
var vm = this;
|
||||
for(var instanceName in CKEDITOR.instances) {
|
||||
CKEDITOR.instances[instanceName].destroy(true);
|
||||
}
|
||||
if (vm.panels.edit.visible) {
|
||||
CKEDITOR.replace( "text", {
|
||||
customConfig: '/static/js/ckeditor-conf.js'
|
||||
} );
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
0
myapp/templates/robots.txt
Normal file
0
myapp/templates/robots.txt
Normal file
@@ -4,11 +4,16 @@
|
||||
<body>
|
||||
<section id="app">
|
||||
|
||||
<div class="container">
|
||||
{% include 'navbar.html' %}
|
||||
|
||||
<div class="container">
|
||||
{% block body %}
|
||||
{% endblock body %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{% endblock %}
|
||||
|
||||
{% include 'footer.html' %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ __email__ = 'remizoffalex@mail.ru'
|
||||
__url__ = 'https://remizoffalex.ru'
|
||||
|
||||
from myapp import app
|
||||
from flask import Flask, render_template, request
|
||||
from flask import Flask, render_template, request, Response
|
||||
|
||||
from . import forms, models
|
||||
|
||||
@@ -19,23 +19,44 @@ def index():
|
||||
return body
|
||||
|
||||
|
||||
@app.route('/edit', methods=['GET', 'POST'])
|
||||
def edit():
|
||||
@app.route('/page')
|
||||
def page():
|
||||
pagedata = {}
|
||||
pagedata['title'] = app.config['TITLE']
|
||||
pagedata['form'] = forms.PageEdit(
|
||||
request.form,
|
||||
data={
|
||||
pagedata['page'] = {
|
||||
'title': 'Заголовок страницы',
|
||||
'text': '''<p>Текст</p>
|
||||
<p class="alert alert-danger">Внимание!</p>'''
|
||||
}
|
||||
)
|
||||
if request.method == 'POST':
|
||||
if pagedata['form'].validate():
|
||||
pagedata['title'] = pagedata['form'].title.data
|
||||
pagedata['text'] = pagedata['form'].text.data
|
||||
body = render_template('page.html', pagedata=pagedata)
|
||||
return body
|
||||
body = render_template('edit.html', pagedata=pagedata)
|
||||
return body
|
||||
|
||||
|
||||
@app.route("/robots.txt")
|
||||
def robots_txt():
|
||||
body = render_template("robots.txt")
|
||||
return Response(body, mimetype='text/plain')
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@app.errorhandler(404)
|
||||
def error_missing(exception):
|
||||
pagedata = {}
|
||||
error_message = "Не судьба..."
|
||||
return render_template("error.html", error_code=404, error_message=error_message, pagedata=pagedata), 404
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@app.errorhandler(403)
|
||||
def error_unauthorized(exception):
|
||||
pagedata = {}
|
||||
error_message = "У вас нет прав"
|
||||
return render_template("error.html", error_code=403, error_message=error_message, pagedata=pagedata), 403
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
@app.errorhandler(500)
|
||||
def error_crash(exception):
|
||||
pagedata = {}
|
||||
error_message = "Вот незадача..."
|
||||
return render_template("error.html", error_code=500, error_message=error_message, pagedata=pagedata), 500
|
||||
|
||||
Reference in New Issue
Block a user