Merge pull request #159 from Simple2B/svyat/feat/version_form

Svyat/feat/version form
This commit is contained in:
Svyatoslav Artymovych 2023-06-16 17:11:18 +03:00 committed by GitHub
commit 5576e59678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 139 additions and 17 deletions

View File

@ -54,3 +54,43 @@ def fork_book(book: m.Book, label: str, about: str):
copy_book_version(book_copy, version)
return version
def fork_version(book: m.Book, label: str, about: str, version: m.BookVersion):
book_copy: m.Book = m.Book(
label=label, about=about, user_id=current_user.id, original_book_id=book.id
)
log(log.INFO, "Create fork of book [%s]", book)
book_copy.save()
active_version = m.BookVersion(
semver="Active", book_id=book_copy.id, is_active=True
).save()
log(log.INFO, "Create new version for book [%s]", book)
active_version.save()
root_collection = m.Collection(
label="Root Collection", version_id=active_version.id, is_root=True
).save()
# access groups
editor_access_group = create_editor_group(book_id=book_copy.id)
moderator_access_group = create_moderator_group(book_id=book_copy.id)
access_groups = [editor_access_group, moderator_access_group]
for access_group in access_groups:
m.BookAccessGroups(book_id=book_copy.id, access_group_id=access_group.id).save()
m.CollectionAccessGroups(
collection_id=root_collection.id, access_group_id=access_group.id
).save()
# -------------
# tags
for tag in book.tags:
m.BookTags(tag_id=tag.id, book_id=book_copy.id).save()
# ----
for collection in version.root_collection.active_children:
recursive_copy_collection(
collection, root_collection.id, active_version.id, False, book=book_copy
)

View File

@ -19,4 +19,4 @@ from .vote import VoteForm
from .comment import CreateCommentForm, DeleteCommentForm, EditCommentForm
from .permission import EditPermissionForm
from .version import EditVersionForm, DeleteVersionForm, CreateVersionForm
from .fork import ForkBookForm
from .fork import ForkBookForm, ForkVersionForm

View File

@ -6,3 +6,9 @@ from wtforms.validators import DataRequired
class ForkBookForm(FlaskForm):
label = StringField("Fork Label", [DataRequired()])
about = StringField("Fork About")
class ForkVersionForm(FlaskForm):
version_id = StringField("Version ID", [DataRequired()])
label = StringField("Fork Label", [DataRequired()])
about = StringField("Fork About")

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,8 @@
{% include 'book/modals/delete_sub_collection_modal.html' %}
{% include 'book/modals/add_section_modal.html' %}
{% include 'book/modals/delete_section_modal.html' %}
{% else %}
{% include 'book/modals/fork_version_modal.html' %}
{% endif %}
<div class="flex">
@ -139,11 +141,16 @@
</p>
{% if version %}
<div class="flex w-full justify-between items-center">
<p class="flex text-s font-bold mb-3">
<!-- prettier-ignore -->
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 mr-3"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" /> </svg>
Version: {{ version.semver.title() }}
</p>
<button type="button" data-modal-target="fork-version-modal" data-modal-toggle="fork-version-modal" class="border-2 border-black focus:outline-none font-medium rounded-lg text-sm px-3 py-2 text-center mr-2 mb-2 dark:border-white dark:text-white dark:focus:ring-white">
+ Create a new fork
</button>
</div>
{% endif %}
<div class="flex justify-between item-center">

View File

@ -36,7 +36,7 @@
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p>
Last updated on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}}
Last updated on {{book.active_version.updated_at.strftime('%B %d, %Y')}}
</p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">

View File

@ -0,0 +1,46 @@
<!-- Add book modal -->
<!-- prettier-ignore-->
<div id="fork-version-modal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-[150] hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative w-full max-w-2xl max-h-full">
<!-- Modal content -->
<form action="{{ url_for('book.fork_specific_version', book_id=book.id) }}" method="post" class="prevent-submit-on-enter relative bg-white rounded-lg shadow dark:bg-gray-700">
{{ form_hidden_tag() }}
<input type="hidden" name="version_id" id="version_id" value="{{version.id}}" />
<!-- Modal header -->
<div class="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600">
<h3 class="text-xl font-semibold text-gray-900 dark:text-white"> Create a new fork of version "{{ version.semver.title() }}"</h3>
<button id="modalAddCloseButton" data-modal-hide="fork-version-modal" type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white"> <svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path> </svg> </button>
</div>
<!-- Modal body -->
<div class="p-6">
<p class="dark:text-white mb-6">
A fork is a copy of a book. Forking a book allows you to freely experiment with changes without affecting the original project.
</p>
<p class="dark:text-white">
By default, forks are named the same as their upstream project. You can customize the name to distinguish it further.
</p>
</div>
<div class="p-6 space-y-6 pt-0">
<div>
<div class="col-span-6 sm:col-span-3 mb-2">
<label for="label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Label</label >
<input value="{{book.label}}" type="text" name="label" id="label" maxlength="256" minlength="6" maxlength="256" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Name" required />
</div>
<div class="col-span-6 sm:col-span-3">
<label for="about" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" >About</label >
<textarea type="text" name="about" id="about" maxlength="640" class="max-h-40 shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Optional">{{book.about}}</textarea>
</div>
</div>
</div>
<div class="p-6 pt-0">
<p class="dark:text-white">
You are creating a fork in your personal account.
</p>
</div>
<!-- Modal footer -->
<div class="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
<button name="submit" type="submit" class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Create fork</button>
</div>
</form>
</div>
</div>

View File

@ -67,7 +67,7 @@
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p>
Last updated on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}}
Last updated on {{book.active_version.updated_at.strftime('%B %d, %Y')}}
</p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">

View File

@ -54,7 +54,7 @@
<dt class="mb-2"><a class="flex flex-col" href="{{url_for('book.collection_view',book_id=book.id)}}">{{book.label}}</a></dt>
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}} </p>
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.active_version.updated_at.strftime('%B %d, %Y')}} </p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">
<span class="book-star-block space-x-0.5 flex items-center">

View File

@ -54,7 +54,7 @@
<dt class="mb-2"><a class="flex flex-col pb-4" href="{{url_for('book.collection_view',book_id=book.id)}}">{{book.label}}</a></dt>
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}} </p>
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.active_version.updated_at.strftime('%B %d, %Y')}} </p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">
<span class="book-star-block space-x-0.5 flex items-center">

View File

@ -42,7 +42,7 @@
<dt class="mb-2"><a class="flex flex-col pb-4" href="{{url_for('book.collection_view',book_id=book.id)}}">{{book.label}}</a></dt>
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}} </p>
<p> Last updated by <a href="{{url_for('user.profile',user_id=book.owner.id)}}" class=" text-blue-500 {% if book.owner.is_deleted %}line-through{% endif %}">{{book.owner.username}}</a> on {{book.active_version.updated_at.strftime('%B %d, %Y')}} </p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">
<span class="book-star-block space-x-0.5 flex items-center">

View File

@ -53,7 +53,7 @@
<dd class="flex flex-col md:flex-row text-lg font-semibold text-gray-500 md:text-lg dark:text-gray-400">
{% if book.versions %}
<p>
Last updated on {{book.versions[-1].updated_at.strftime('%B %d, %Y')}}
Last updated on {{book.active_version.updated_at.strftime('%B %d, %Y')}}
</p>
{% endif %}
<div class="flex ml-auto align-center justify-center space-x-3">

View File

@ -80,7 +80,7 @@ def collection_create(book_id: int, collection_id: int | None = None):
collection = collection.filter_by(parent_id=collection_id)
else:
collection = collection.filter_by(
parent_id=book.versions[-1].root_collection.id
parent_id=book.active_version.root_collection.id
)
collection = collection.first()
@ -102,7 +102,7 @@ def collection_create(book_id: int, collection_id: int | None = None):
collection: m.Collection = m.Collection(
label=label,
about=form.about.data,
parent_id=book.versions[-1].root_collection.id,
parent_id=book.active_version.root_collection.id,
version_id=book.active_version.id,
position=position,
)

View File

@ -2,7 +2,7 @@ from flask import flash, redirect, url_for
from flask_login import login_required
from app import models as m, db, forms as f
from app.controllers.fork import fork_book
from app.controllers.fork import fork_book, fork_version
from app.controllers.error_flashes import create_error_flash
from app.logger import log
from .bp import bp
@ -23,3 +23,26 @@ def fork(book_id):
log(log.ERROR, "Fork book errors: [%s]", form.errors)
create_error_flash(form)
return redirect(redirect_url)
@bp.route("/<int:book_id>/fork_version", methods=["POST"])
@login_required
def fork_specific_version(book_id):
form: f.ForkVersionForm = f.ForkVersionForm()
book = db.session.get(m.Book, book_id)
redirect_url = url_for("book.statistic_view", book_id=book.id, active_tab="forks")
if form.validate_on_submit():
book_version: m.BookVersion = db.session.get(
m.BookVersion, form.version_id.data
)
if not book_version or book_version.book_id != book_id:
flash("Invalid version data", "warning")
else:
fork_version(book, form.label.data, form.about.data, book_version)
flash("Success!", "success")
return redirect(redirect_url)
else:
log(log.ERROR, "Fork book errors: [%s]", form.errors)
create_error_flash(form)
return redirect(redirect_url)