diff options
Diffstat (limited to 'python/fatcat_web')
| -rw-r--r-- | python/fatcat_web/editing_routes.py | 212 | ||||
| -rw-r--r-- | python/fatcat_web/routes.py | 26 | ||||
| -rw-r--r-- | python/fatcat_web/templates/container_edit.html | 4 | ||||
| -rw-r--r-- | python/fatcat_web/templates/container_view.html | 2 | ||||
| -rw-r--r-- | python/fatcat_web/templates/edit_macros.html | 8 | ||||
| -rw-r--r-- | python/fatcat_web/templates/editgroup_view.html | 24 | ||||
| -rw-r--r-- | python/fatcat_web/templates/entity_macros.html | 17 | 
7 files changed, 213 insertions, 80 deletions
| diff --git a/python/fatcat_web/editing_routes.py b/python/fatcat_web/editing_routes.py index 6895a4cd..88fb4557 100644 --- a/python/fatcat_web/editing_routes.py +++ b/python/fatcat_web/editing_routes.py @@ -13,6 +13,7 @@ from fatcat_web.auth import handle_token_login, handle_logout, load_user, handle  from fatcat_web.cors import crossdomain  from fatcat_web.search import *  from fatcat_web.forms import * +from fatcat_web.entity_helpers import *  ### Helper Methods ########################################################## @@ -49,80 +50,177 @@ def form_editgroup_get_or_create(api, edit_form):  ### Views ################################################################### -@app.route('/container/create', methods=['GET', 'POST']) -@login_required -def container_create(): -    form = ContainerEntityForm() +def generic_entity_edit(entity_type, edit_template, existing_ident, editgroup_id): +    """ + +    existing (entity) + +    Create: existing blank, ident blank, editgroup optional +    Update: ident set + +    Need to handle: +    - editgroup not set (need to create one) +    - creating entity from form +    - updating an existing ident +    - updating an existing editgroup/ident + +    Views: +    - /container/create +    - /container/<ident>/edit +    - /editgroup/<editgroup_id>/container/<ident>/edit + +    Helpers: +    - get_editgroup_revision(editgroup, entity_type, ident) -> None or entity +     +    TODO: prev_rev interlock +    """ + +    # fetch editgroup (if set) or 404 +    editgroup = None +    if editgroup_id: +        try: +            editgroup = api.get_editgroup(editgroup_id) +        except ApiException as ae: +            abort(ae.status) + +    # fetch entity (if set) or 404 +    existing = None +    existing_edit = None +    if editgroup and existing_ident: +        existing, existing_edit = generic_get_editgroup_entity(editgroup, entity_type, existing_ident) +    elif existing_ident: +        existing = generic_get_entity(entity_type, existing_ident) + +    # parse form (if submitted)      status = 200 +    if entity_type == 'container': +        form = ContainerEntityForm() +    else: +        raise NotImplementedError +      if form.is_submitted():          if form.validate_on_submit():              # API on behalf of user              user_api = auth_api(session['api_token']) -            eg = form_editgroup_get_or_create(user_api, form) -            if eg: -                # no merge or anything hard to do; just create the entity -                entity = form.to_entity() -                try: -                    edit = user_api.create_container(eg.editgroup_id, entity) -                except ApiException as ae: -                    app.log.warning(ae) -                    abort(ae.status) -                # redirect to new entity -                return redirect('/container/{}'.format(edit.ident)) +            if not editgroup: +                editgroup = form_editgroup_get_or_create(user_api, form) + +            if editgroup: +                # check that editgroup is edit-able +                if editgroup.changelog_index != None: +                    flash("Editgroup already merged") +                    abort(400) + +                if not existing_ident: # it's a create +                    entity = form.to_entity() +                    try: +                        if entity_type == 'container': +                            edit = user_api.create_container(editgroup.editgroup_id, entity) +                        else: +                            raise NotImplementedError +                    except ApiException as ae: +                        app.log.warning(ae) +                        abort(ae.status) +                    return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident)) +                else: # it's an update +                    # all the tricky logic is in the update method +                    form.update_entity(existing) +                    # do we need to try to delete the current in-progress edit first? +                    # TODO: some danger of wiping database state here is +                    # "updated edit" causes, eg, a 4xx error. Better to allow +                    # this in the API itself. For now, form validation *should* +                    # catch most errors, and if not editor can hit back and try +                    # again. This means, need to allow failure of deletion. +                    if existing_edit: +                        # need to clear revision on object or this becomes just +                        # a "update pointer" edit +                        existing.revision = None +                        try: +                            if entity_type == 'container': +                                user_api.delete_container_edit(editgroup.editgroup_id, existing_edit.edit_id) +                            else: +                                raise NotImplementedError +                        except ApiException as ae: +                            if ae.status == 404: +                                pass +                            else: +                                abort(ae.status) +                    try: +                        if entity_type == 'container': +                            edit = user_api.update_container(editgroup.editgroup_id, existing.ident, existing) +                        else: +                            raise NotImplementedError +                    except ApiException as ae: +                        app.log.warning(ae) +                        abort(ae.status) +                    return redirect('/editgroup/{}/{}/{}'.format(editgroup.editgroup_id, entity_type, edit.ident))              else:                  status = 400          elif form.errors:              status = 400              app.log.info("form errors (did not validate): {}".format(form.errors)) -    else: -        editgroup_id = session.get('active_editgroup_id', None) -        form.editgroup_id.data = editgroup_id -    return render_template('container_create.html', form=form), status + +    else: # form is not submitted +        if existing: +            if entity_type == 'container': +                form = ContainerEntityForm.from_entity(existing) +            else: +                raise NotImplementedError + +        if not editgroup_id: +            form.editgroup_id.data = session.get('active_editgroup_id', None) + +    return render_template(edit_template, form=form, +        existing_ident=existing_ident, editgroup=editgroup), status + +def generic_edit_delete(entity_type, editgroup_id, edit_id): +    # fetch editgroup (if set) or 404 +    editgroup = None +    if editgroup_id: +        try: +            editgroup = api.get_editgroup(editgroup_id) +        except ApiException as ae: +            abort(ae.status) + +    # API on behalf of user +    user_api = auth_api(session['api_token']) +     +    # do the deletion +    try: +        if entity_type == 'container': +            user_api.delete_container_edit(editgroup.editgroup_id, edit_id) +        else: +            raise NotImplementedError +    except ApiException as ae: +        if ae.status == 404: +            pass +        else: +            abort(ae.status) +    return redirect("/editgroup/{}".format(editgroup_id)) + + +@app.route('/container/create', methods=['GET', 'POST']) +@login_required +def container_create(): +    return generic_entity_edit('container', 'container_create.html', None, None)  @app.route('/container/<ident>/edit', methods=['GET', 'POST'])  @login_required  def container_edit(ident): -    # TODO: prev_rev interlock -    try: -        entity = api.get_container(ident) -    except ApiException as ae: -        abort(ae.status) -    status = 200 -    form = ContainerEntityForm() -    if form.is_submitted(): -        if form.validate_on_submit(): -            # API on behalf of user -            user_api = auth_api(session['api_token']) -            eg = form_editgroup_get_or_create(user_api, form) -            if eg: -                # all the tricky logic is in the update method -                form.update_entity(entity) -                try: -                    edit = user_api.update_container(eg.editgroup_id, entity.ident, entity) -                except ApiException as ae: -                    app.log.warning(ae) -                    abort(ae.status) -                # redirect to entity revision -                # TODO: container_rev_view -                return redirect('/container/{}'.format(edit.ident)) -            else: -                status = 400 -        elif form.errors: -            status = 400 -            app.log.info("form errors (did not validate): {}".format(form.errors)) -    else: -        form = ContainerEntityForm.from_entity(entity) -    if not form.is_submitted(): -        editgroup_id = session.get('active_editgroup_id', None) -        form.editgroup_id.data = editgroup_id -    return render_template('container_edit.html', form=form, entity=entity), status +    return generic_entity_edit('container', 'container_edit.html', ident, None) + +@app.route('/editgroup/<editgroup_id>/container/<ident>/edit', methods=['GET', 'POST']) +@login_required +def container_editgroup_edit(editgroup_id, ident): +    return generic_entity_edit('container', 'container_edit.html', ident, editgroup_id) + +@app.route('/editgroup/<editgroup_id>/container/edit/<edit_id>/delete', methods=['POST', 'DELETE']) +@login_required +def container_edit_delete(editgroup_id, edit_id): +    return generic_edit_delete('container', editgroup_id, edit_id)  @app.route('/creator/<ident>/edit', methods=['GET'])  def creator_edit(ident): -    try: -        entity = api.get_creator(ident) -    except ApiException as ae: -        abort(ae.status)      return render_template('entity_edit.html'), 404  @app.route('/creator/create', methods=['GET']) diff --git a/python/fatcat_web/routes.py b/python/fatcat_web/routes.py index 3dcf05ae..20036608 100644 --- a/python/fatcat_web/routes.py +++ b/python/fatcat_web/routes.py @@ -14,6 +14,7 @@ from fatcat_web.auth import handle_token_login, handle_logout, load_user, handle  from fatcat_web.cors import crossdomain  from fatcat_web.search import *  from fatcat_web.hacks import strip_extlink_xml, wayback_suffix +from fatcat_web.entity_helpers import *  ### Generic Entity Views #################################################### @@ -192,10 +193,7 @@ def work_lookup():  @app.route('/container/<ident>', methods=['GET'])  def container_view(ident): -    try: -        entity = api.get_container(ident) -    except ApiException as ae: -        abort(ae.status) +    entity = generic_get_entity('container', ident)      if entity.issnl:          try: @@ -213,7 +211,23 @@ def container_view(ident):      if entity.state == "active":          entity.es = container_to_elasticsearch(entity, force_bool=False)      return render_template('container_view.html', -        container=entity, container_stats=stats) +        container=entity, editgroup_id=None, container_stats=stats) + +@app.route('/container/rev/<revision_id>', methods=['GET']) +def container_revision_view(ident): +    entity = generic_get_entity_revision('container', revision_id) +    return render_template('container_view.html', container=entity, editgroup=None) + +@app.route('/editgroup/<editgroup_id>/container/<ident>', methods=['GET']) +def container_editgroup_view(editgroup_id, ident): +    try: +        editgroup = api.get_editgroup(editgroup_id) +    except ApiException as ae: +        abort(ae.status) +    entity, edit = generic_get_editgroup_entity(editgroup, 'container', ident) +    if entity.state == "deleted": +        return render_template('deleted_entity.html', entity=entity, entity_type="container", editgroup=editgroup) +    return render_template('container_view.html', container=entity, editgroup=editgroup)  @app.route('/creator/<ident>', methods=['GET'])  def creator_view(ident): @@ -333,6 +347,7 @@ def editgroup_view(ident):      auth_to = dict(          submit=False,          accept=False, +        edit=False,          annotate=False,      )      if session.get('editor'): @@ -340,6 +355,7 @@ def editgroup_view(ident):          auth_to['annotate'] = True          if user.is_admin or user.editor_id == eg.editor_id:              auth_to['submit'] = True +            auth_to['edit'] = True          if user.is_admin:              auth_to['accept'] = True      return render_template('editgroup_view.html', editgroup=eg, diff --git a/python/fatcat_web/templates/container_edit.html b/python/fatcat_web/templates/container_edit.html index 553bedb6..6a08b1ae 100644 --- a/python/fatcat_web/templates/container_edit.html +++ b/python/fatcat_web/templates/container_edit.html @@ -6,12 +6,12 @@  <div class="ui segment">  <h1 class="ui header">Edit Container Entity</h1> -<form class="ui form" id="edit_container_form" method="POST" action="/container/{{ entity.ident }}/edit"> +<form class="ui form" id="edit_container_form" method="POST" action="{% if editgroup %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/container/{{ existing_ident }}/edit">  {% endblock %}    {{ form.hidden_tag() }}    <br> -  {{ edit_macros.editgroup_dropdown(form) }} +  {{ edit_macros.editgroup_dropdown(form, editgroup) }}    <h3 class="ui dividing header">The Basics</h3>    <br> diff --git a/python/fatcat_web/templates/container_view.html b/python/fatcat_web/templates/container_view.html index 619bd647..ef4335fb 100644 --- a/python/fatcat_web/templates/container_view.html +++ b/python/fatcat_web/templates/container_view.html @@ -124,7 +124,7 @@  {% endif %}  </div> -{{ entity_macros.fatcat_bits(entity, "container", "") }} +{{ entity_macros.fatcat_bits(entity, "container", "", editgroup) }}  </div>  </div> diff --git a/python/fatcat_web/templates/edit_macros.html b/python/fatcat_web/templates/edit_macros.html index 9bd14596..66da04e7 100644 --- a/python/fatcat_web/templates/edit_macros.html +++ b/python/fatcat_web/templates/edit_macros.html @@ -35,13 +35,13 @@  </div>  {%- endmacro %} -{% macro editgroup_dropdown(form) -%} +{% macro editgroup_dropdown(form, editgroup=None) -%}    <div class="ui accordion"> -    <div class="{% if not editgroup_id %}active{% endif %} title"> +    <div class="{% if not editgroup %}active{% endif %} title">        <h3><i class="dropdown icon"></i>Editgroup Meta</h3>      </div> -    <div class="{% if not editgroup_id %}active{% endif %} content"> -      {% if editgroup_id %} +    <div class="{% if not editgroup %}active{% endif %} content"> +      {% if editgroup %}          <p>You have an editgroup in progress, and this edit will be included by          default. You can override this below.        {% else %} diff --git a/python/fatcat_web/templates/editgroup_view.html b/python/fatcat_web/templates/editgroup_view.html index 8b454bb2..52b55d43 100644 --- a/python/fatcat_web/templates/editgroup_view.html +++ b/python/fatcat_web/templates/editgroup_view.html @@ -3,7 +3,7 @@  {% block body %} -{% macro edit_list(edits, entity_type, entity_name) -%} +{% macro edit_list(auth_to, editgroup, edits, entity_type, entity_name) -%}  <div class="{% if edits %}active{% endif %} title">    <h3><i class="dropdown icon"></i>{{ entity_name }} Edits ({{ edits|count }})</h3>  </div><div class="{% if edits %}active{% endif %} content" style="padding-bottom: 0.5em;"> @@ -22,6 +22,14 @@            {% else %}              updated            {% endif %} +          <a href="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/{{ edit.ident }}">[view edit]</a> +          {% if auth_to.edit and not editgroup.changelog_index and not editgroup.submitted %} +            <a href="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/{{ edit.ident }}/edit" style="color: green;">[re-edit]</a> +            <form id="submit_edit_delete" method="POST" action="/editgroup/{{ editgroup.editgroup_id }}/{{ entity_type }}/edit/{{ edit.edit_id }}/delete" style="display:inline;"> +                <input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/> +                <input type="submit" value="[delete-edit]" style="background:none; color: red; border: none; padding:9;font-weight:bold;cursor:pointer;"></input> +            </form> +          {% endif %}          </div>          {% if edit.revision %}            Revision: <small><code>{{ edit.revision }}</code></small> @@ -99,13 +107,13 @@ reviewable bundle.  <br><br clear="all">  <div class="ui styled fluid accordion"> -  {{ edit_list(editgroup.edits.releases, "release", "Release") }} -  {{ edit_list(editgroup.edits.works, "work", "Work") }} -  {{ edit_list(editgroup.edits.containers, "container", "Container") }} -  {{ edit_list(editgroup.edits.creators, "creator", "Creator") }} -  {{ edit_list(editgroup.edits.files, "file", "File") }} -  {{ edit_list(editgroup.edits.filesets, "fileset", "File Set") }} -  {{ edit_list(editgroup.edits.webcaptures, "webcapture", "Web Capture") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.releases, "release", "Release") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.works, "work", "Work") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.containers, "container", "Container") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.creators, "creator", "Creator") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.files, "file", "File") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.filesets, "fileset", "File Set") }} +  {{ edit_list(auth_to, editgroup, editgroup.edits.webcaptures, "webcapture", "Web Capture") }}  </div>  <br> diff --git a/python/fatcat_web/templates/entity_macros.html b/python/fatcat_web/templates/entity_macros.html index a2b2f996..77d4ff81 100644 --- a/python/fatcat_web/templates/entity_macros.html +++ b/python/fatcat_web/templates/entity_macros.html @@ -1,7 +1,18 @@ -{% macro fatcat_bits(entity, entity_type, expand="") -%} +{% macro fatcat_bits(entity, entity_type, expand="", editgroup=None) -%} -{% if entity.state == "wip" %} +{% if entity.state == None and editgroup.editgroup_id %} +<div class="ui segment pink inverted attached"> +  <b>Edit In Progress</b> +  <p>You are viewing this entity as of a specific editgroup (which may or may not have been merged yet): +  <b><a href="/editgroup/{{ editgroup.editgroup_id }}">{{ editgroup.editgroup_id }}</a></b> +</div> +{% elif entity.state == None and entity.ident == None %} +<div class="ui segment pink inverted attached"> +  <b>Revision</b> +  <p>You are viewing a specific revision of an entity. +</div> +{% elif entity.state == "wip" %}  <div class="ui segment pink inverted attached">    <b>Work In Progress</b>    <p>This entity has not been "accepted" into the official database yet. @@ -19,7 +30,7 @@  </div>  <div class="two ui buttons bottom attached"> -  <a href="/{{ entity_type }}/{{ entity.ident }}/edit" class="ui blue button">Edit Metadata</a> +  <a href="{% if editgroup %}/editgroup/{{ editgroup.editgroup_id }}{% endif %}/{{ entity_type }}/{{ entity.ident }}/edit" class="ui blue button">Edit Metadata</a>    <a href="/{{ entity_type }}/{{ entity.ident }}/history" class="ui button">View History</a>  </div>  {%- endmacro %} | 
