view farol/producttree.py @ 139:584587a000a2

Propagate ProductID and GroupID updates in the whole document
author Benoît Allard <benoit.allard@greenbone.net>
date Mon, 27 Oct 2014 15:08:29 +0100
parents 64a6e69d54fc
children 1d63a532ccce
line wrap: on
line source
# -*- encoding: utf-8 -*-
# Description:
# Web stuff about the ProductTree
#
# Authors:
# BenoƮt Allard <benoit.allard@greenbone.net>
#
# Copyright:
# Copyright (C) 2014 Greenbone Networks GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.

"""\
Web-stuff about the producttree
"""

from functools import wraps

from flask import Blueprint, render_template, redirect, url_for, abort, request, flash

from farolluz.cvrf import (CVRFProductBranch, CVRFFullProductName,
    CVRFRelationship, CVRFGroup)
from .session import document_required, get_current

producttree = Blueprint('producttree', __name__)

def producttree_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if get_current()._producttree is None:
            flash('Operation invalid without producttree', 'warning')
            return redirect(url_for('document.view'))
        return f(*args, **kwargs)
    return decorated_function

@producttree.route('/create', methods=['POST'])
@document_required
def create():
    get_current().createProductTree()
    return redirect(url_for('.view'))

@producttree.route('/')
@document_required
@producttree_required
def view():
    cvrf = get_current()
    return render_template('producttree/view.j2',
                           producttree=cvrf._producttree, cvrf=cvrf)

@producttree.route('/delete', methods=['POST'])
@producttree_required
def delete():
    if not cvrf.isProductTreeOrphan():
        flash('Not deleting the Product Tree, some Products are mentionned in the document', 'danger')
        return redirect(url_for('.view'))
    get_current()._producttree = None
    return redirect(url_for('document.view'))

@producttree.route('/branch/<path:path>/edit', methods=['GET', 'POST'])
@document_required
@producttree_required
def edit_branch(path):
    path = [int(p) for p in path.split('/')]
    cvrf = get_current()
    ptree = cvrf._producttree
    try: branch = cvrf._producttree.getBranch(path)
    except (ValueError, IndexError): abort(404)
    if request.method != 'POST':
        branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches(branch)]
        return render_template('producttree/edit_branch.j2',
                               _type=branch._type, name=branch._name, parentpath=branch.getParentPath(),
                               branches=branches, types=branch.TYPES)

    pbranch = ptree
    if request.form['parent']:
        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
        except (ValueError, IndexError): abort(404)

    if pbranch is not branch.getParent():
        # We have to 're-link' the element ...
        # 1. unlink
        branch.unlink()
        # 2. set the new parent
        branch.link(pbranch)

    branch._type = request.form['type']
    branch._name = request.form['name']
    return redirect(url_for('.view'))

@producttree.route('/branch/add', methods=['GET', 'POST'])
@document_required
@producttree_required
def add_branch():
    cvrf = get_current()
    ptree = cvrf._producttree
    if request.method != 'POST':
        branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches()]
        return render_template('producttree/edit_branch.j2',
                               action='Add',
                               branches=branches, types=CVRFProductBranch.TYPES)

    pbranch = ptree
    if request.form['parent']:
        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')])
        except (ValueError, IndexError): abort(404)
    branch = CVRFProductBranch(request.form['type'], request.form['name'], pbranch)
    return redirect(url_for('.view'))

@producttree.route('/branch/<path:path>/del', methods=['POST'])
@document_required
@producttree_required
def del_branch(path):
    path = [int(p) for p in path.split('/')]
    cvrf = get_current()
    ptree = cvrf._producttree
    try: branch = cvrf._producttree.getBranch(path)
    except (ValueError, IndexError):
        flash('Cannot find Branch', 'danger')
        abort(404)
    if branch._childs:
        flash('Cannot delete a branch with childs', 'danger')
        abort(403)
    branch.unlink()
    del branch
    return redirect(url_for('.view'))


@producttree.route('/product/<productid>')
@document_required
@producttree_required
def view_product(productid):
    cvrf = get_current()
    try:
        product = cvrf.getProductForID(productid)
    except KeyError:
        abort(404)
    return render_template('producttree/view_product.j2',
        product=product, groups=[g for g in cvrf._producttree._groups if productid in g._productids],
        cvrf=cvrf)

@producttree.route('/product/<productid>/edit', methods=['GET', 'POST'])
@document_required
@producttree_required
def edit_product(productid):
    cvrf = get_current()
    ptree = cvrf._producttree
    try:
        product = cvrf.getProductForID(productid)
    except KeyError:
        abort(404)

    if request.method != 'POST':
        current_rel = ''
        if product.isRelationship():
            current_rel = str(ptree._relationships.index(product.getCurrentRelationship()))
        leaves = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getOrphanedBranches(product)]
        rels = [('', '')] + [(ptree.getNameOfRelationship(r), str(i)) for i, r in ptree.getOrphanedRelationships(product)]
        return render_template('producttree/edit_product.j2', product=product, action='Update', orphaned_leaves=leaves, orphaned_relationships=rels, current_rel=current_rel)

    if request.form['parent_branch'] and request.form['parent_relationship']:
        flash('Cannot set a parent branch and parent relationship', 'danger')
        return redirect(url_for('.edit_product', productid=productid))

    oldp = product._parent
    if request.form['parent_branch']:
        try: pbranch = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
        except (ValueError, IndexError): abort(404)
        if pbranch is not oldp:
            # Gonna be funny, needs re-link
            product.unlink()
            # And Link again
            product.link(pbranch)
    elif request.form['parent_relationship']:
        prel = ptree._relationships[int(request.form['parent_relationship'])]
        if prel is not oldp:
            # Unlink
            product.unlink()
            # Link again
            product.link(prel)
    else:
        if ptree is not oldp:
            # Unlink
            product.unlink()
            # Link again
            product.link(ptree)

    if (request.form['productid'] != product._productid) and not cvrf.isProductOrphan(product._productid):
        flash('Also updating the ProductID for %s in this Document' % request.form['name'], 'info')
        cvrf.changeProductID(product._productid, request.form['productid'])
    product._productid = request.form['productid']
    product._name = request.form['name']
    product._cpe = request.form['cpe'] or None
    return redirect(url_for('.view'))

@producttree.route('/product/add', methods=['GET', 'POST'])
@document_required
@producttree_required
def add_product():
    cvrf = get_current()
    ptree = cvrf._producttree

    if request.method != 'POST':
        product=CVRFFullProductName('', '', ptree)
        leaves = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getOrphanedBranches()]
        rels = [('', '')] + [(ptree.getNameOfRelationship(r), str(i)) for i, r in ptree.getOrphanedRelationships()]
        return render_template('producttree/edit_product.j2', product=product, action='Add', orphaned_leaves=leaves, orphaned_relationships=rels, current_rel='')

    if request.form['parent_branch'] and request.form['parent_relationship']:
        flash('Cannot set a parent branch and parent relationship', 'danger')
        return redirect(url_for('.add_product'))

    parent = ptree
    if request.form['parent_branch']:
        try: parent = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')])
        except (ValueError, IndexError): abort(404)
    elif request.form['parent_relationship']:
        parent = ptree._relationships[int(request.form['parent_relationship'])]

    product = CVRFFullProductName(request.form['productid'], request.form['name'], parent, request.form['cpe'] or None)
    ptree.addProduct(product)
    return redirect(url_for('.view'))

@producttree.route('/product/<productid>/del', methods=['POST'])
@document_required
@producttree_required
def del_product(productid):
    cvrf = get_current()
    ptree = cvrf._producttree
    try:
        product = cvrf.getProductForID(productid)
    except KeyError:
        abort(404)
    if not cvrf.isProductOrphan(product._productid):
        flash('Not deleting the Product, it is used in the Document.', 'danger')
        return redirect(url_for('.view_product', productid=product._productid))
    product.unlink()
    ptree._products.remove(product)
    del product
    return redirect(url_for('.view'))


@producttree.route('/relationship/<int:index>/edit', methods=['GET', 'POST'])
@document_required
@producttree_required
def edit_relationship(index):
    cvrf = get_current()
    ptree = cvrf._producttree
    try:
        rel = ptree._relationships[index]
    except IndexError:
        abort(404)

    if request.method != 'POST':
        return render_template('producttree/edit_relationship.j2', productreference=rel._productreference, relationtype=rel._relationtype, relatestoproductreference=rel._relatestoproductreference, types=rel.TYPES)

    rel._productreference = request.form['productreference']
    rel._relationtype = request.form['relationtype']
    rel._relatestoproductreference = request.form['relatestoproductreference']
    return redirect(url_for('.view'))

@producttree.route('/relationship/add', methods=['GET', 'POST'])
@document_required
@producttree_required
def add_relationship():
    cvrf = get_current()
    ptree = cvrf._producttree
    if len(ptree._products) < 2:
        flash('You need to have at least two products to create a relationship', 'warning')
        return redirect(url_for('.add_product'))

    if request.method != 'POST':
        return render_template('producttree/edit_relationship.j2', action='Add', types=CVRFRelationship.TYPES)

    prodid1 = request.form['productreference']
    prodid2 = request.form['relatestoproductreference']

    if prodid1 == prodid2:
        flash('You cannot create a relationship with the same product twice', 'danger')
        return redirect(url_for('.add_relationship'))

    rel = CVRFRelationship(prodid1, request.form['relationtype'], prodid2)
    ptree.addRelationship(rel)
    return redirect(url_for('.view'))

@producttree.route('/relationship/<int:index>/del', methods=['POST'])
@document_required
@producttree_required
def del_relationship(index):
    rels = get_current()._producttree._relationships
    if not (0 <= index < len(rels)):
        flash('Relationship not found', 'danger')
        abort(404)
    rel = rels[index]

    if not rel.isOrphaned():
        flash('Not able to delete Relationship with Product', 'danger')
        abort(403)

    del rels[index]
    return redirect(url_for('.view'))


@producttree.route('/group/<groupid>/edit', methods=['GET', 'POST'])
@document_required
@producttree_required
def edit_group(groupid):
    cvrf = get_current()
    try:
        group = cvrf.getGroupForID(groupid)
    except KeyError:
        abort(404)
    if request.method != 'POST':
        return render_template('producttree/edit_group.j2', groupid=group._groupid, description=group._description, productids=group._productids)

    if (request.form['groupid'] != group._groupid) and not cvrf.isGroupOrphan(group._groupid):
        flash('Also updating the groupid in the whole document.', 'info')
        cvrf.changeGroupID(group._groupid, request.form['groupid'])
    group._groupid = request.form['groupid']
    group.setDescription(request.form['description'] or None)
    group._productids = []
    for productid in request.form.getlist('products'):
        group.addProductID(productid)
    return redirect(url_for('.view'))

@producttree.route('/group/add', methods=['GET', 'POST'])
@document_required
@producttree_required
def add_group():
    if request.method != 'POST':
        return render_template('producttree/edit_group.j2', action='Add')

    group = CVRFGroup(request.form['groupid'])
    group.setDescription(request.form['description'] or None)
    for productid in request.form.getlist('products'):
        group.addProductID(productid)
    get_current()._producttree.addGroup(group)
    return redirect(url_for('.view'))

@producttree.route('/group/<groupid>/del', methods=['POST'])
@document_required
@producttree_required
def del_group(groupid):
    cvrf = get_current()
    try:
        group = get_current().getGroupForID(groupid)
    except KeyError:
        flash('Group not found', 'danger')
        abort(404)

    if not cvrf.isGroupOrphan(group._groupid):
        flash('Not deleting group, it is mentionned in the document.', 'danger')
        return redirect(url_for('.view'))

    cvrf._producttree._groups.remove(group)
    return redirect(url_for('.view'))

http://farol.wald.intevation.org