benoit@0: # -*- encoding: utf-8 -*- benoit@0: # Description: benoit@0: # Web stuff about the ProductTree benoit@0: # benoit@0: # Authors: benoit@0: # BenoƮt Allard benoit@0: # benoit@0: # Copyright: benoit@0: # Copyright (C) 2014 Greenbone Networks GmbH benoit@0: # benoit@0: # This program is free software; you can redistribute it and/or benoit@0: # modify it under the terms of the GNU General Public License benoit@0: # as published by the Free Software Foundation; either version 2 benoit@0: # of the License, or (at your option) any later version. benoit@0: # benoit@0: # This program is distributed in the hope that it will be useful, benoit@0: # but WITHOUT ANY WARRANTY; without even the implied warranty of benoit@0: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the benoit@0: # GNU General Public License for more details. benoit@0: # benoit@0: # You should have received a copy of the GNU General Public License benoit@0: # along with this program; if not, write to the Free Software benoit@0: # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. benoit@0: benoit@0: """\ benoit@0: Web-stuff about the producttree benoit@0: """ benoit@0: benoit@0: from functools import wraps benoit@0: benoit@0: from flask import Blueprint, render_template, redirect, url_for, abort, request, flash benoit@0: benoit@0: from farolluz.cvrf import (CVRFProductBranch, CVRFFullProductName, benoit@0: CVRFRelationship, CVRFGroup) benoit@0: from .session import document_required, get_current benoit@0: benoit@0: producttree = Blueprint('producttree', __name__) benoit@0: benoit@0: def producttree_required(f): benoit@0: @wraps(f) benoit@0: def decorated_function(*args, **kwargs): benoit@0: if get_current()._producttree is None: benoit@0: flash('Operation invalid without producttree', 'warning') benoit@22: return redirect(url_for('document.view')) benoit@0: return f(*args, **kwargs) benoit@0: return decorated_function benoit@0: benoit@0: @producttree.route('/create', methods=['POST']) benoit@0: @document_required benoit@0: def create(): benoit@0: get_current().createProductTree() benoit@45: return redirect(url_for('.view')) benoit@0: benoit@45: @producttree.route('/') benoit@45: @document_required benoit@45: @producttree_required benoit@45: def view(): benoit@86: cvrf = get_current() benoit@45: return render_template('producttree/view.j2', benoit@86: producttree=cvrf._producttree, cvrf=cvrf) benoit@0: benoit@99: @producttree.route('/delete', methods=['POST']) benoit@99: @producttree_required benoit@99: def delete(): benoit@135: if not cvrf.isProductTreeOrphan(): benoit@135: flash('Not deleting the Product Tree, some Products are mentionned in the document', 'danger') benoit@135: return redirect(url_for('.view')) benoit@99: get_current()._producttree = None benoit@99: return redirect(url_for('document.view')) benoit@99: benoit@0: @producttree.route('/branch//edit', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def edit_branch(path): benoit@0: path = [int(p) for p in path.split('/')] benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@78: try: branch = cvrf._producttree.getBranch(path) benoit@78: except (ValueError, IndexError): abort(404) benoit@0: if request.method != 'POST': benoit@0: branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches(branch)] benoit@85: return render_template('producttree/edit_branch.j2', benoit@85: _type=branch._type, name=branch._name, parentpath=branch.getParentPath(), benoit@85: branches=branches, types=branch.TYPES) benoit@0: benoit@0: pbranch = ptree benoit@0: if request.form['parent']: benoit@78: try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')]) benoit@78: except (ValueError, IndexError): abort(404) benoit@0: benoit@0: if pbranch is not branch.getParent(): benoit@0: # We have to 're-link' the element ... benoit@0: # 1. unlink benoit@0: branch.unlink() benoit@0: # 2. set the new parent benoit@82: branch.link(pbranch) benoit@0: benoit@0: branch._type = request.form['type'] benoit@0: branch._name = request.form['name'] benoit@45: return redirect(url_for('.view')) benoit@0: benoit@0: @producttree.route('/branch/add', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def add_branch(): benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@0: if request.method != 'POST': benoit@0: branches = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getNotTerminalBranches()] benoit@85: return render_template('producttree/edit_branch.j2', benoit@85: action='Add', benoit@85: branches=branches, types=CVRFProductBranch.TYPES) benoit@0: benoit@0: pbranch = ptree benoit@0: if request.form['parent']: benoit@78: try: pbranch = ptree.getBranch([int(p) for p in request.form['parent'].split('/')]) benoit@78: except (ValueError, IndexError): abort(404) benoit@0: branch = CVRFProductBranch(request.form['type'], request.form['name'], pbranch) benoit@45: return redirect(url_for('.view')) benoit@0: benoit@80: @producttree.route('/branch//del', methods=['POST']) benoit@80: @document_required benoit@80: @producttree_required benoit@80: def del_branch(path): benoit@80: path = [int(p) for p in path.split('/')] benoit@80: cvrf = get_current() benoit@80: ptree = cvrf._producttree benoit@80: try: branch = cvrf._producttree.getBranch(path) benoit@80: except (ValueError, IndexError): benoit@80: flash('Cannot find Branch', 'danger') benoit@80: abort(404) benoit@80: if branch._childs: benoit@80: flash('Cannot delete a branch with childs', 'danger') benoit@80: abort(403) benoit@80: branch.unlink() benoit@80: del branch benoit@80: return redirect(url_for('.view')) benoit@80: benoit@80: benoit@0: @producttree.route('/product/') benoit@0: @document_required benoit@0: @producttree_required benoit@0: def view_product(productid): benoit@0: cvrf = get_current() benoit@0: try: benoit@0: product = cvrf.getProductForID(productid) benoit@136: except KeyError: benoit@0: abort(404) benoit@106: return render_template('producttree/view_product.j2', benoit@106: product=product, groups=[g for g in cvrf._producttree._groups if productid in g._productids], benoit@106: cvrf=cvrf) benoit@0: benoit@0: @producttree.route('/product//edit', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def edit_product(productid): benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@0: try: benoit@0: product = cvrf.getProductForID(productid) benoit@0: except KeyError: benoit@0: abort(404) benoit@0: benoit@0: if request.method != 'POST': benoit@0: current_rel = '' benoit@0: if product.isRelationship(): benoit@0: current_rel = str(ptree._relationships.index(product.getCurrentRelationship())) benoit@0: leaves = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getOrphanedBranches(product)] benoit@0: rels = [('', '')] + [(ptree.getNameOfRelationship(r), str(i)) for i, r in ptree.getOrphanedRelationships(product)] benoit@0: return render_template('producttree/edit_product.j2', product=product, action='Update', orphaned_leaves=leaves, orphaned_relationships=rels, current_rel=current_rel) benoit@0: benoit@0: if request.form['parent_branch'] and request.form['parent_relationship']: benoit@0: flash('Cannot set a parent branch and parent relationship', 'danger') benoit@0: return redirect(url_for('.edit_product', productid=productid)) benoit@0: benoit@0: oldp = product._parent benoit@0: if request.form['parent_branch']: benoit@78: try: pbranch = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')]) benoit@78: except (ValueError, IndexError): abort(404) benoit@0: if pbranch is not oldp: benoit@0: # Gonna be funny, needs re-link benoit@0: product.unlink() benoit@82: # And Link again benoit@82: product.link(pbranch) benoit@0: elif request.form['parent_relationship']: benoit@0: prel = ptree._relationships[int(request.form['parent_relationship'])] benoit@0: if prel is not oldp: benoit@82: # Unlink benoit@0: product.unlink() benoit@82: # Link again benoit@82: product.link(prel) benoit@0: else: benoit@0: if ptree is not oldp: benoit@82: # Unlink benoit@0: product.unlink() benoit@82: # Link again benoit@82: product.link(ptree) benoit@0: benoit@139: if (request.form['productid'] != product._productid) and not cvrf.isProductOrphan(product._productid): benoit@139: flash('Also updating the ProductID for %s in this Document' % request.form['name'], 'info') benoit@139: cvrf.changeProductID(product._productid, request.form['productid']) benoit@0: product._productid = request.form['productid'] benoit@0: product._name = request.form['name'] benoit@0: product._cpe = request.form['cpe'] or None benoit@45: return redirect(url_for('.view')) benoit@0: benoit@0: @producttree.route('/product/add', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def add_product(): benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@0: benoit@0: if request.method != 'POST': benoit@0: product=CVRFFullProductName('', '', ptree) benoit@0: leaves = [('', '')] + [(b.getName(), b.getPath(True)) for b in ptree.getOrphanedBranches()] benoit@0: rels = [('', '')] + [(ptree.getNameOfRelationship(r), str(i)) for i, r in ptree.getOrphanedRelationships()] benoit@0: return render_template('producttree/edit_product.j2', product=product, action='Add', orphaned_leaves=leaves, orphaned_relationships=rels, current_rel='') benoit@0: benoit@0: if request.form['parent_branch'] and request.form['parent_relationship']: benoit@0: flash('Cannot set a parent branch and parent relationship', 'danger') benoit@62: return redirect(url_for('.add_product')) benoit@0: benoit@0: parent = ptree benoit@0: if request.form['parent_branch']: benoit@83: try: parent = ptree.getBranch([int(p) for p in request.form['parent_branch'].split('/')]) benoit@78: except (ValueError, IndexError): abort(404) benoit@0: elif request.form['parent_relationship']: benoit@0: parent = ptree._relationships[int(request.form['parent_relationship'])] benoit@0: benoit@0: product = CVRFFullProductName(request.form['productid'], request.form['name'], parent, request.form['cpe'] or None) benoit@0: ptree.addProduct(product) benoit@45: return redirect(url_for('.view')) benoit@0: benoit@84: @producttree.route('/product//del', methods=['POST']) benoit@84: @document_required benoit@84: @producttree_required benoit@84: def del_product(productid): benoit@84: cvrf = get_current() benoit@84: ptree = cvrf._producttree benoit@84: try: benoit@84: product = cvrf.getProductForID(productid) benoit@84: except KeyError: benoit@84: abort(404) benoit@135: if not cvrf.isProductOrphan(product._productid): benoit@135: flash('Not deleting the Product, it is used in the Document.', 'danger') benoit@135: return redirect(url_for('.view_product', productid=product._productid)) benoit@84: product.unlink() benoit@84: ptree._products.remove(product) benoit@84: del product benoit@84: return redirect(url_for('.view')) benoit@84: benoit@84: benoit@0: @producttree.route('/relationship//edit', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def edit_relationship(index): benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@0: try: benoit@0: rel = ptree._relationships[index] benoit@0: except IndexError: benoit@0: abort(404) benoit@0: benoit@0: if request.method != 'POST': benoit@0: return render_template('producttree/edit_relationship.j2', productreference=rel._productreference, relationtype=rel._relationtype, relatestoproductreference=rel._relatestoproductreference, types=rel.TYPES) benoit@0: benoit@0: rel._productreference = request.form['productreference'] benoit@0: rel._relationtype = request.form['relationtype'] benoit@0: rel._relatestoproductreference = request.form['relatestoproductreference'] benoit@45: return redirect(url_for('.view')) benoit@0: benoit@0: @producttree.route('/relationship/add', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def add_relationship(): benoit@0: cvrf = get_current() benoit@0: ptree = cvrf._producttree benoit@0: if len(ptree._products) < 2: benoit@0: flash('You need to have at least two products to create a relationship', 'warning') benoit@0: return redirect(url_for('.add_product')) benoit@0: benoit@0: if request.method != 'POST': benoit@0: return render_template('producttree/edit_relationship.j2', action='Add', types=CVRFRelationship.TYPES) benoit@0: benoit@0: prodid1 = request.form['productreference'] benoit@0: prodid2 = request.form['relatestoproductreference'] benoit@0: benoit@0: if prodid1 == prodid2: benoit@0: flash('You cannot create a relationship with the same product twice', 'danger') benoit@0: return redirect(url_for('.add_relationship')) benoit@0: benoit@0: rel = CVRFRelationship(prodid1, request.form['relationtype'], prodid2) benoit@0: ptree.addRelationship(rel) benoit@45: return redirect(url_for('.view')) benoit@0: benoit@87: @producttree.route('/relationship//del', methods=['POST']) benoit@87: @document_required benoit@87: @producttree_required benoit@87: def del_relationship(index): benoit@87: rels = get_current()._producttree._relationships benoit@87: if not (0 <= index < len(rels)): benoit@87: flash('Relationship not found', 'danger') benoit@87: abort(404) benoit@87: rel = rels[index] benoit@87: benoit@87: if not rel.isOrphaned(): benoit@87: flash('Not able to delete Relationship with Product', 'danger') benoit@87: abort(403) benoit@87: benoit@87: del rels[index] benoit@87: return redirect(url_for('.view')) benoit@87: benoit@87: benoit@0: @producttree.route('/group//edit', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def edit_group(groupid): benoit@139: cvrf = get_current() benoit@0: try: benoit@139: group = cvrf.getGroupForID(groupid) benoit@0: except KeyError: benoit@0: abort(404) benoit@0: if request.method != 'POST': benoit@0: return render_template('producttree/edit_group.j2', groupid=group._groupid, description=group._description, productids=group._productids) benoit@0: benoit@139: if (request.form['groupid'] != group._groupid) and not cvrf.isGroupOrphan(group._groupid): benoit@139: flash('Also updating the groupid in the whole document.', 'info') benoit@139: cvrf.changeGroupID(group._groupid, request.form['groupid']) benoit@0: group._groupid = request.form['groupid'] benoit@0: group.setDescription(request.form['description'] or None) benoit@0: group._productids = [] benoit@0: for productid in request.form.getlist('products'): benoit@0: group.addProductID(productid) benoit@45: return redirect(url_for('.view')) benoit@0: benoit@0: @producttree.route('/group/add', methods=['GET', 'POST']) benoit@0: @document_required benoit@0: @producttree_required benoit@0: def add_group(): benoit@0: if request.method != 'POST': benoit@0: return render_template('producttree/edit_group.j2', action='Add') benoit@0: benoit@0: group = CVRFGroup(request.form['groupid']) benoit@0: group.setDescription(request.form['description'] or None) benoit@0: for productid in request.form.getlist('products'): benoit@0: group.addProductID(productid) benoit@0: get_current()._producttree.addGroup(group) benoit@45: return redirect(url_for('.view')) benoit@88: benoit@88: @producttree.route('/group//del', methods=['POST']) benoit@88: @document_required benoit@88: @producttree_required benoit@88: def del_group(groupid): benoit@88: cvrf = get_current() benoit@88: try: benoit@88: group = get_current().getGroupForID(groupid) benoit@88: except KeyError: benoit@88: flash('Group not found', 'danger') benoit@88: abort(404) benoit@88: benoit@135: if not cvrf.isGroupOrphan(group._groupid): benoit@135: flash('Not deleting group, it is mentionned in the document.', 'danger') benoit@135: return redirect(url_for('.view')) benoit@135: benoit@88: cvrf._producttree._groups.remove(group) benoit@88: return redirect(url_for('.view')) benoit@88: