diff --git a/data/quests.py b/data/quests.py index 4d33818..812c8e1 100644 --- a/data/quests.py +++ b/data/quests.py @@ -1,5 +1,6 @@ import sqlalchemy from flask_login import UserMixin +from datetime import date from .db_session import SqlAlchemyBase diff --git a/forms/conf_delete_project.py b/forms/conf_delete_project.py new file mode 100644 index 0000000..b66946f --- /dev/null +++ b/forms/conf_delete_project.py @@ -0,0 +1,8 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField +from wtforms.validators import DataRequired + + +class DeleteProjectForm(FlaskForm): + conf = StringField('', validators=[DataRequired()]) + submit = SubmitField('Подтвердить') diff --git a/forms/new_project.py b/forms/project.py similarity index 71% rename from forms/new_project.py rename to forms/project.py index c5d9e08..5350afd 100644 --- a/forms/new_project.py +++ b/forms/project.py @@ -3,8 +3,10 @@ from wtforms import StringField, SubmitField, TextAreaField, FileField from wtforms.validators import DataRequired -class NewProjectForm(FlaskForm): +class ProjectForm(FlaskForm): name = StringField('Название', validators=[DataRequired()]) description = TextAreaField('Описание') logo = FileField('Логотип') submit = SubmitField('Создать') + del_photo = SubmitField('Удалить фотографию') + save = SubmitField('Сохранить') diff --git a/main.py b/main.py index 6b47e98..f44b53f 100644 --- a/main.py +++ b/main.py @@ -15,8 +15,9 @@ from forms.edit_profile import EditProfileForm from forms.login import LoginForm from forms.find_project import FindProjectForm from forms.register import RegisterForm -from forms.new_project import NewProjectForm +from forms.project import ProjectForm from forms.recovery import RecoveryForm, NewPasswordForm +from forms.conf_delete_project import DeleteProjectForm from data.users import User from data.quests import Quests @@ -43,6 +44,63 @@ def base(): return redirect('/projects') +@app.route('/project//edit', methods=['GET', 'POST']) +def edit_project(id_project): + if current_user.is_authenticated: + data_session = db_session.create_session() + current_project = data_session.query(Projects).filter(Projects.id == id_project).first() + if current_project: + staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all() + if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)): + list_users = list( + map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all())) + staff = list(map(lambda x: get_user_data(x), data_session.query(User).filter( + User.id.in_(list(map(lambda x: x.user, staff)))).all())) if staff else [] + form = ProjectForm() + if form.save.data: + new_staff = [] + for i in list_users: + if request.form.getlist(f"choose_{i['login']}") and i['id'] != current_user.id: + new_staff.append(i) + if i not in staff: + new_staffer = StaffProjects( + user=i['id'], + project=current_project.id, + role='user', + permission=3 + ) + data_session.add(new_staffer) + data_session.commit() + if sorted(new_staff, key=lambda x: x['id']) != sorted(staff, key=lambda x: x['id']): + for i in staff: + if i not in new_staff: + data_session.delete(data_session.query(StaffProjects).filter( + StaffProjects.user == i['id'], StaffProjects.project == current_project.id).first()) + data_session.commit() + if form.logo.data: + current_project.photo = save_project_logo(form.logo.data) + data_session.commit() + current_project.name = form.name.data + current_project.description = form.description.data + data_session.commit() + return redirect(f'/project/{current_project.id}/edit') + if form.del_photo.data: + os.remove(current_project.photo) + current_project.photo = 'static/images/none_project.png' + data_session.commit() + return redirect(f'/project/{current_project.id}/edit') + form.name.data = current_project.name + form.description.data = current_project.description + return render_template('edit_project.html', title='Изменение проекта', form=form, list_users=list_users, + staff=staff, project=current_project) + else: + abort(403) + else: + abort(404) + else: + return redirect('/login') + + @app.route('/project/') def project(id_project): if current_user.is_authenticated: @@ -51,6 +109,7 @@ def project(id_project): if current_project: staff = data_session.query(StaffProjects).filter(StaffProjects.project == current_project.id).all() if current_user.id == current_project.creator or current_user.id in list(map(lambda x: x.user, staff)): + return render_template('project.html', project=current_project, title=current_project.name) else: abort(403) @@ -103,22 +162,29 @@ def recovery(): return redirect('/') -@app.route('/projects/delete/', methods=['GET', 'POST']) +@app.route('/project//delete', methods=['GET', 'POST']) def delete_project(id_project): if current_user.is_authenticated: data_session = db_session.create_session() project_del = data_session.query(Projects).filter(Projects.id == id_project).first() if project_del: if project_del.creator == current_user.id: - staff = data_session.query(StaffProjects).filter(StaffProjects.project == id_project).all() - for i in staff: - data_session.delete(i) - if 'none_project' not in project_del.photo: - os.remove(project_del.photo) - data_session.delete(project_del) - data_session.commit() - data_session.close() - return redirect('/projects') + form = DeleteProjectForm() + if form.validate_on_submit(): + if form.conf.data != f'delete/{project_del.name}': + return render_template('delete_project.html', title='Удаление проекта', form=form, + project=project_del, + message='Вы не правильно ввели фразу') + staff = data_session.query(StaffProjects).filter(StaffProjects.project == id_project).all() + for i in staff: + data_session.delete(i) + if 'none_project' not in project_del.photo: + os.remove(project_del.photo) + data_session.delete(project_del) + data_session.commit() + return redirect('/projects') + return render_template('delete_project.html', title='Удаление проекта', form=form, project=project_del, + message='') else: abort(403) else: @@ -149,7 +215,7 @@ def user_view(_login): @app.route('/projects/new', methods=['GET', 'POST']) def new_project(): if current_user.is_authenticated: - form = NewProjectForm() + form = ProjectForm() data_session = db_session.create_session() list_users = list( map(lambda x: get_user_data(x), data_session.query(User).filter(User.id != current_user.id).all())) @@ -175,9 +241,7 @@ def new_project(): ) data_session.add(new_staffer) data_session.commit() - data_session.close() return redirect('/projects') - data_session.close() return render_template('new_project.html', title='Новый проект', form=form, list_users=list_users) else: return redirect('/login') @@ -231,7 +295,6 @@ def profile(): os.remove(current_user.photo) user.photo = 'static/images/none_logo.png' data_session.commit() - data_session.close() if form.validate_on_submit(): data_session = db_session.create_session() user = data_session.query(User).filter(User.id == current_user.id).first() @@ -249,7 +312,6 @@ def profile(): user.about = form.about.data user.birthday = form.birthday.data data_session.commit() - data_session.close() return redirect('/profile') return render_template('profile.html', title='Профиль', form=form, message='') else: @@ -273,7 +335,6 @@ def login(): user = data_session.query(User).filter(User.email == form.login.data).first() if not user: user = data_session.query(User).filter(User.login == form.login.data).first() - data_session.close() if user and user.check_password(form.password.data): if user.activated: login_user(user, remember=form.remember_me.data) @@ -326,7 +387,6 @@ def register(): user.set_password(form.password.data) data_session.add(user) data_session.commit() - data_session.close() token = s.dumps(form.email.data) link_conf = url_for('confirmation', token=token, _external=True) mail(f'Для завершения регистрации пройдите по ссылке: {link_conf}', form.email.data, @@ -346,7 +406,6 @@ def confirmation(token): if user: user.activated = True data_session.commit() - data_session.close() return redirect('/login?message=Почта успешно подтверждена') else: return redirect('/login?message=Пользователь не найден&danger=True') @@ -357,17 +416,21 @@ def confirmation(token): if users: list(map(lambda x: data_session.delete(x), users)) data_session.commit() - data_session.close() return redirect('/login?message=Срок действия ссылки истек, данные удалены&danger=True') +@app.errorhandler(500) +def internal_server_error(error): + return render_template('page_error.html', title='Ошибка сервера', error='500', message='Технические шоколадки') + + @app.errorhandler(404) def page_not_found(error): return render_template('page_error.html', title='Страница не найдена', error='404', message='Страница не найдена') @app.errorhandler(403) -def page_not_found(error): +def access_error(error): return render_template('page_error.html', title='Ошибка доступа', error='403', message='Доступ сюда запрещен') diff --git a/static/css/base.css b/static/css/base.css index 0ca6565..bbce475 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -62,7 +62,7 @@ body { align-items: center; } .nav_user_name { - margin-left: 10px; + margin-left: 1vw; align-self: center; } .nav_chapter_text { @@ -90,10 +90,10 @@ body { height: 100%; } body::-webkit-scrollbar { - width: 12px; /* ширина scrollbar */ + width: 0.8vw; /* ширина scrollbar */ } body::-webkit-scrollbar-thumb { background-color: #d49d51; /* цвет плашки */ - border-radius: 20px; /* закругления плашки */ - border: 3px solid #ffffff; + border-radius: 5vw; /* закругления плашки */ + border: 0.25vw solid #ffffff; } \ No newline at end of file diff --git a/static/css/delete_project.css b/static/css/delete_project.css new file mode 100644 index 0000000..f073bb8 --- /dev/null +++ b/static/css/delete_project.css @@ -0,0 +1,60 @@ +.delete_project_page { + height: 60vw; + background-color: #dcb495; +} +.form_block { + width: 100%; + height: 60vw; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; +} +.form_data { + display: flex; + flex-direction: column; + margin-left: 2%; +} +.input_data { + color: #000000; + border: 0.1vw solid #595008; + height: 4.5vw; + min-height: 4.5vw; + width: 35vw; + background-color: #dbc3af; + border-radius: 4.5vw; + font-size: 1.3vw; + display: inline-flex; + align-items: center; +} +.form_label { + margin-top: 10px; + font-size: 1.3vw; + color: #ffffff; + font-weight: bold; +} +.delete_project_button { + margin-left: 15px; + width: 25vw; + height: 5vw; + background-color: #000000; + color: #ffffff; + border-radius: 5vw; + vertical-align: middle; + font-size: 1.5vw; +} +form { + display: flex; + align-items: flex-end; + justify-content: center; +} +.conf_text { + color: #ff0000; +} +.header_title { + text-align: center; + color: #000000; + font-size: 3.5vw; + width: 100%; + margin-bottom: 15px; +} \ No newline at end of file diff --git a/static/css/edit_project.css b/static/css/edit_project.css new file mode 100644 index 0000000..8d93b48 --- /dev/null +++ b/static/css/edit_project.css @@ -0,0 +1,149 @@ +.edit_project_page { + height: 120vw; + background-color: #dcb495; +} +.form_data, .form_data_button { + display: flex; + flex-direction: column; + margin-left: 2%; +} +.form_data_button { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +.input_data { + color: #000000; + border: 0.1vw solid #595008; + height: 4.5vw; + min-height: 4.5vw; + width: 35vw; + background-color: #dbc3af; + border-radius: 4.5vw; + font-size: 1.3vw; + display: inline-flex; + align-items: center; +} +.input_button { + width: 35vw; + height: 5vw; + border-radius: 5vw; + vertical-align: middle; +} +.form_label { + margin-top: 10px; + font-size: 1.3vw; + color: #ffffff; + font-weight: bold; +} +.description { + border-radius: 2vw !important; + width: 50vw; +} +.padding_data { + padding-top: 1vw; + padding-left: 1vw; +} +.label_data { + padding-left: 0.8vw; + width: 50vw; +} +.project_button, .delete_button, .delete_project_link { + margin-top: 15px; + width: 35vw; + height: 5vw; + background-color: #000000; + color: #ffffff; + border-radius: 5vw; + vertical-align: middle; + font-size: 1.5vw; +} +.collaborator_block { + width: 30%; + height: 20vw; + background-color: #EDCBB0; + border-radius: 2vw; + overflow-y: auto; +} +.user { + width: 30vw; + height: 3.5vw; + background-color: #ffffff; + border: 2px solid #9E795A; + border-radius: 3vw; + margin-top: 5px; + display: inline-flex; + justify-content: space-between; +} +.user_logo { + margin-left: 3px; + width: 3vw; + height: 3vw; + border-radius: 5vw; + background-color: #000000; +} +.user_names { + margin-left: 9px; + margin-top: 10px; + overflow-x: auto; +} +.name_form_block { + width: 100%; + display: flex; + flex-direction: column; + align-items: center; +} +.data_form_block { + width: 100%; + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; +} +.buttons_form_block { + width: 45%; + display: flex; + flex-direction: column; + align-items: center; +} +.staff_form_block, .collaborator_block { + margin-top: 10px; + width: 60%; + height: 20vw; + display: flex; + align-items: center; + justify-content: center; +} +.staff_block { + margin: 5%; + width: 90%; + height: 20vw; +} +.choose_user { + align-self: flex-end; + margin-bottom: 1.1vw; + width: 10%; +} +.user_data { + display: inline-flex; + align-items: center; + justify-content: flex-start; + flex-direction: row; +} +.delete_project_link { + background-color: #ff3f3f; +} +.delete_project_link:hover { + background-color: #ff3f3f; + text-decoration: none; + color: #ffffff; +} +.delete_project_link_text { + width: 35vw; + height: 5vw; + display: flex; + align-items: center; + justify-content: center; + margin-top: 10px; +} \ No newline at end of file diff --git a/static/css/project.css b/static/css/project.css index 1c32441..f97fa2a 100644 --- a/static/css/project.css +++ b/static/css/project.css @@ -22,7 +22,34 @@ flex-direction: column; align-items: center; justify-content: space-between; + color: #dcb495 + #a8886f + #f5d3b8 + #a65b1e + #d49d51 + #face7d + #ffe8d6 + #a8876b + #fff2e8 + #c79b77 + #d69d5c; } .name_project { font-size: 3vw; +} +.edit_block { + display: flex; + align-items: center; + justify-content: center; +} +.edit_button { + width: 4.5vw; + height: 4.5vw; + display: flex; + align-items: center; + justify-content: center; +} +.edit_button_image { + height: 3vw; + width: 3vw; } \ No newline at end of file diff --git a/static/images/pen_b.png b/static/images/pen_b.png new file mode 100644 index 0000000..ac9e99e Binary files /dev/null and b/static/images/pen_b.png differ diff --git a/static/images/pen_w.png b/static/images/pen_w.png new file mode 100644 index 0000000..f94d2a9 Binary files /dev/null and b/static/images/pen_w.png differ diff --git a/static/js/project.js b/static/js/project.js new file mode 100644 index 0000000..b75b539 --- /dev/null +++ b/static/js/project.js @@ -0,0 +1,2 @@ +var edit_button = document.getElementById("edit_button"); +edit_button.href = String(window.location.href) + '/edit'; \ No newline at end of file diff --git a/templates/delete_project.html b/templates/delete_project.html new file mode 100644 index 0000000..535aee9 --- /dev/null +++ b/templates/delete_project.html @@ -0,0 +1,26 @@ + +{% extends "base.html" %} {% block content %} +{% set name = 'delete/' + project.name %} +
+
+

Удаление проекта

+
+ {{ form.hidden_tag() }} +
+ + {{ form.conf(class="input_data", type="conf", placeholder=name) }} + {% for error in form.conf.errors %} + + {% endfor %} +
+ {{ form.submit(type="submit", class="delete_project_button") }} +
+ {% if message != '' %} + + {% endif %} +
+
+{% endblock %} \ No newline at end of file diff --git a/templates/edit_project.html b/templates/edit_project.html new file mode 100644 index 0000000..3469af9 --- /dev/null +++ b/templates/edit_project.html @@ -0,0 +1,68 @@ + +{% extends "base.html" %} {% block content %} +
+
+
+ {{ form.hidden_tag() }} +
+
+ + {{ form.name(class="input_data label_data", type="name", placeholder='your project name') }} + {% for error in form.name.errors %} + + {% endfor %} +
+
+ + {{ form.description(class="input_data description padding_data", type="description", placeholder='your project description') }} + {% for error in form.description.errors %} + + {% endfor %} +
+
+
+
+
+
+ {% for user in list_users %} +
+
+ +

{{user['name']}}

+
+ {% if user not in staff %} + + {% else %} + + {% endif %} +
+ {% endfor %} +
+
+
+
+ {% if 'none' in project.photo %} +
+ + {{ form.logo(class="input_data padding_data", type="file") }} + {% for error in form.logo.errors %} + + {% endfor %} +
+ {% else %} +
+ {{ form.del_photo(type="submit", class="delete_button") }} +
+ {% endif %} +
+ {{ form.save(type="submit", class="project_button") }} +
+ + + +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/templates/project.html b/templates/project.html index 6b893a9..bbc086b 100644 --- a/templates/project.html +++ b/templates/project.html @@ -3,7 +3,9 @@
- + + +
@@ -25,4 +27,5 @@
+ {% endblock %} \ No newline at end of file