#!/usr/bin/perl -wT # -*- Mode: perl; indent-tabs-mode: nil -*- # # The contents of this file are subject to the Mozilla Public # License Version 1.1 (the "License"); you may not use this file # except in compliance with the License. You may obtain a copy of # the License at http://www.mozilla.org/MPL/ # # Software distributed under the License is distributed on an "AS # IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or # implied. See the License for the specific language governing # rights and limitations under the License. # # The Original Code is the Bugzilla Testopia System. # # The Initial Developer of the Original Code is Greg Hendricks. # Portions created by Greg Hendricks are Copyright (C) 2006 # Novell. All Rights Reserved. # # Contributor(s): Greg Hendricks # Michael Hight # Garrett Braden # Scott Sudweeks # Brian Kramer use strict; use lib "."; use Bugzilla; use Bugzilla::Util; use Bugzilla::Config; use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Testopia::Util; use Bugzilla::Testopia::TestRun; use Bugzilla::Testopia::Product; use Bugzilla::Testopia::Classification; use Bugzilla::Testopia::Environment; use Bugzilla::Testopia::Environment::Element; use Bugzilla::Testopia::Environment::Property; use Bugzilla::Testopia::Environment::Category; use JSON; use Data::Dumper; Bugzilla->login(LOGIN_REQUIRED); local our $cgi = Bugzilla->cgi; local our $template = Bugzilla->template; local our $vars = {}; print $cgi->header; ThrowUserError('testopia-permission-denied') unless Bugzilla->user->in_group('Testers'); my $action = $cgi->param('action') || ''; my $env_id = $cgi->param('env_id') || 0; if($action eq 'getChildren'){ my $json = new JSON; my $data = $json->jsonToObj($cgi->param('data')); my $node = $data->{'node'}; my $id = $node->{'objectId'}; my $type = $node->{'widgetId'}; detaint_natural($id); trick_taint($type); for ($type){ /classification/ && do { get_products($id); }; /product/ && do { get_categories($id); }; /category/ && do { get_category_element($id) }; /element/ && do { get_element_children($id); }; /property/ && do { get_validexp_json($id) }; } } elsif($action eq 'edit'){ my $type = $cgi->param('type'); my $id = $cgi->param('id'); for ($type){ /category/ && do { edit_category($id); }; /element/ && do { edit_element($id); }; /property/ && do { edit_property($id); }; /validexp/ && do { edit_validexp($id); }; } } elsif($action eq 'do_edit'){ my $type = $cgi->param('type'); my $id = $cgi->param('id'); for ($type){ /category/ && do { do_edit_category($id); }; /element/ && do { do_edit_element($id); }; /property/ && do { do_edit_property($id); }; /validexp/ && do { do_edit_validexp($id); }; } } elsif($action eq 'createChild'){ my $json = new JSON; my $data = $json->jsonToObj($cgi->param('data')); my $node = $data->{'parent'}; my $id = $node->{'objectId'}; my $type = $node->{'widgetId'}; my $create_type = $data->{'data'}->{'createType'}; $type = 'child' if ($create_type && $type !~ /category/); detaint_natural($id); trick_taint($type); for ($type){ /product/ && do { add_category($id); }; /category/ && do { add_element($id); }; /child/ && do { add_element($id,1); }; /element/ && do { add_property($id); }; /property/ && do { add_validexp($id); }; } } elsif($action eq 'removeNode'){ my $json = new JSON; my $data = $json->jsonToObj($cgi->param('data')); my $node = $data->{'node'}; my $id = $node->{'objectId'}; my $type = $node->{'widgetId'}; detaint_natural($id) unless $type =~ /validexp/; trick_taint($type); for ($type){ /category/ && do { delete_category($id); }; /element/ && do { delete_element($id); }; /property/ && do { delete_property($id); }; /validexp/ && do { delete_validexp($id); }; } } elsif ($action eq 'getcategories'){ my $product_id = $cgi->param('product_id'); detaint_natural($product_id); my $product = Bugzilla::Testopia::Product->new($product_id); exit unless $product && Bugzilla->user->can_see_product($product->name); my $cat = Bugzilla::Testopia::Environment::Category->new({}); my $categories = $cat->get_element_categories_by_product($product_id); my $ret; foreach my $c (@{$categories}){ my $name = $c->name; $name =~ s/|<\/span>//g; $ret .= $c->id.'||'.$name.'|||'; } chop($ret); print $ret; } elsif ($action eq 'getelements'){ my $cat_name = $cgi->param('cat_name'); my $cat = Bugzilla::Testopia::Environment::Category->new($cat_name); my $elements = $cat->get_elements_by_category(); my $ret; foreach my $e (@{$elements}){ my $elem = Bugzilla::Testopia::Environment::Element->new(@$e{'element_id'}); $elem->{'name'} =~ s/|<\/span>//g; $ret .= $elem->{'element_id'}.'||'.$elem->{'name'}.'|||'; } $ret = substr($ret, 0, length($ret) - 3); print $ret; } else{ &display; } sub display{ my $category = Bugzilla::Testopia::Environment::Category->new({'id' => 0}); if (Bugzilla->params->{'useclassification'}){ $vars->{'allhaschild'} = $category->get_all_child_count; $vars->{'toplevel'} = Bugzilla->user->get_selectable_classifications; $vars->{'type'} = 'classification'; } else { $vars->{'toplevel'} = $category->get_env_product_list; $vars->{'type'} = 'product'; } $template->process("testopia/environment/admin/show.html.tmpl", $vars) || print $template->error(); } ########################### ### Tree Helper Methods ### ########################### sub get_products{ my ($class_id) = (@_); my $class = Bugzilla::Testopia::Classification->new($class_id); return unless scalar(grep {$class->id eq $class_id} @{Bugzilla->user->get_selectable_classifications}); print $class->products_to_json; } sub get_categories{ my ($product_id) = (@_); if ($product_id){ my $product = Bugzilla::Testopia::Product->new($product_id); return unless $product->canedit; } my $category = Bugzilla::Testopia::Environment::Category->new({}); print $category->product_categories_to_json($product_id); } sub get_category_element{ my ($id) = (@_); my $category = Bugzilla::Testopia::Environment::Category->new($id); return unless $category->canedit; print $category->elements_to_json; } sub get_element_children { my ($id) = (@_); my $element = Bugzilla::Testopia::Environment::Element->new($id); return unless $element->canedit; print $element->children_to_json; } sub get_validexp_json { my ($id) = (@_); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->canedit; print $property->valid_exp_to_json; } ########################### ### Edit Helper Methods ### ########################### sub edit_category{ my ($id) = (@_); my $category = Bugzilla::Testopia::Environment::Category->new($id); return unless $category->canedit; my $product = Bugzilla::Testopia::Product->new($category->product_id()); $category->{'name'} =~ s/|<\/span>//g; $vars->{'category'} = $category; $vars->{'products'} = $category->get_env_product_list; $vars->{'currentproduct'} = $product ? $product : {'id' => 0, 'name' => '--ANY PRODUCT--'}; $template->process("testopia/environment/admin/category.html.tmpl", $vars) || print $template->error(); } sub edit_element{ my ($id) = (@_); my $element = Bugzilla::Testopia::Environment::Element->new($id); return unless $element->canedit; my $category = Bugzilla::Testopia::Environment::Category->new($element->env_category_id()); my $product = Bugzilla::Testopia::Product->new($category->product_id()); $element->{'name'} =~ s/|<\/span>//g; $vars->{'element'} = $element; $vars->{'products'} = $category->get_env_product_list; $vars->{'currentproduct'} = $product ? $product : {'id' => 0, 'name' => '--ANY PRODUCT--'}; $vars->{'currentcategory'} = $category; $vars->{'categories'} = $category->get_element_categories_by_product($product->{'id'}); unless($element->parent_id() == 0){ my $parent = Bugzilla::Testopia::Environment::Element->new($element->parent_id()); $vars->{'currentelement'} = $parent; } $vars->{'elements'} = $category->get_elements_by_category(); $template->process("testopia/environment/admin/element.html.tmpl", $vars) || print $template->error(); } sub edit_property{ my ($id) = (@_); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->canedit; my $element = Bugzilla::Testopia::Environment::Element->new($property->element_id()); my $cat_id = $element->env_category_id(); my $elmnts = Bugzilla::Testopia::Environment::Category->new($cat_id)->get_elements_by_category(); $vars->{'property'} = $property; $vars->{'elements'} = $elmnts; $vars->{'currentelement'} = $element; $template->process("testopia/environment/admin/property.html.tmpl", $vars); } sub edit_validexp{ my ($id) = (@_); $id =~ /^(\d+)~/; my $property = Bugzilla::Testopia::Environment::Property->new($1); return unless $property->canedit; my @expressions = split /\|/, $property->validexp(); $vars->{'property'} = $property; $vars->{'expressions'} = \@expressions; $template->process("testopia/environment/admin/valid_exp.html.tmpl", $vars); } ############################## ### Do_Edit Helper Methods ### ############################## sub do_edit_category{ my $name = $cgi->param('name'); my $product_id = $cgi->param('product'); my ($id) = (@_); my $category = Bugzilla::Testopia::Environment::Category->new($id); return unless $category->canedit; trick_taint($name); detaint_natural($product_id); my $error_mode_cache = Bugzilla->error_mode; Bugzilla->error_mode(ERROR_MODE_DIE); eval{ validate_selection($product_id, 'id', 'products'); }; Bugzilla->error_mode($error_mode_cache); if ($@ && $product_id != 0){ print '{error:"Invalid product"}'; exit; } $category->set_product($product_id); unless ($category->set_name($name)) { print '{error:"Name already used. Please choose another"}'; exit; } print "{name:\"$name\", product:\"product$product_id\", widget:\"category$id\"}"; } sub do_edit_element{ my ($id) = (@_); # # CGI params # productCombo -> id of product (does not matter what this is, only used to find categories) # categoryCombo -> id of category (if zero, leave the same) # elementCombo -> id of parent element (id of 0 means no parent) # name -> name of the element # my $element = Bugzilla::Testopia::Environment::Element->new($id); return unless $element->canedit; my $cat_id = $cgi->param('categoryCombo'); my $parent_id = $cgi->param('elementCombo'); my $name = $cgi->param('name'); my $parent; detaint_natural($cat_id); detaint_natural($parent_id); trick_taint($name); if ($cat_id){ $element->update_element_category($cat_id); $parent = 'category' . $cat_id; } else { print '{error: "Category does not exist"}'; exit; } if ($parent_id){ $element->update_element_parent($parent_id); $parent = 'element' . $parent_id; } elsif (!$cat_id) { print '{error: "Parent element does not exist"}'; exit; } unless ($element->update_element_name($name)){ print '{error: "Name already taken. Please choose another."}'; exit; } print "{name:\"$name\", parent:\"$parent\", widget:\"element$id\"}"; } sub do_edit_property{ my ($id) = (@_); my $name = $cgi->param('name'); my $element_id = $cgi->param('element'); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->canedit; trick_taint($name); detaint_natural($element_id); my $error_mode_cache = Bugzilla->error_mode; Bugzilla->error_mode(ERROR_MODE_DIE); eval{ validate_selection($element_id, 'element_id', 'test_environment_element'); }; Bugzilla->error_mode($error_mode_cache); if ($@){ print '{error:"Invalid element"}'; exit; } $property->set_element($element_id); unless ($property->set_name($name)) { print '{error:"Name already used. Please choose another"}'; exit; } print "{name:\"$name\", element:\"element$element_id\", widget:\"property$id\"}"; } sub do_edit_validexp{ my ($id) = (@_); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->canedit; my @expressions = $cgi->param('valid_exp'); my $exp = join('|', @expressions); trick_taint($exp); $property->update_property_validexp($exp); display; } ################################### ### Create Child Helper Methods ### ################################### sub add_category{ my ($id) = (@_); my $category = Bugzilla::Testopia::Environment::Category->new({}); if ($id){ my $product = Bugzilla::Testopia::Product->new($id); return unless $product->canedit; } $category->{'product_id'} = $id; $category->{'name'} = 'New category ' . $category->new_category_count; my $new_cid = $category->store(); my $json = '{title:"' . $category->{'name'} . '",'; $json .= 'objectId:"'. $new_cid . '",'; $json .= 'widgetId:"category' . $new_cid . '",'; $json .= 'childIconSrc:"testopia/img/square.gif",'; $json .= 'actionsDisabled:["addCategory","addValue","addProperty"]}'; print $json; } sub add_element{ my ($id, $ischild) = (@_); my $element = Bugzilla::Testopia::Environment::Element->new({}); # If we are adding this element as a child, $id is the parent element's id if ($ischild) { my $parent = Bugzilla::Testopia::Environment::Element->new($id); return unless $parent->canedit; $element->{'env_category_id'} = $parent->env_category_id; } # Otherwise $id is the catagory id else { my $parent = Bugzilla::Testopia::Environment::Category->new($id); return unless $parent->canedit; $element->{'env_category_id'} = $id; } $element->{'name'} = "New element " . $element->new_element_count; $element->{'parent_id'} = $ischild ? $id : 0; $element->{'isprivate'} = 0; my $new_eid = $element->store(); my $json = '{title:"' . $element->{'name'} .'",'; $json .= 'objectId:"'. $new_eid .'",'; $json .= 'widgetId:"element'. $new_eid .'",'; $json .= 'childIconSrc:"testopia/img/circle.gif",'; $json .= 'actionsDisabled:["addCategory","addValue"]}'; print $json; } sub add_property{ my ($id) = (@_); #add new property to element with id=$id my $property = Bugzilla::Testopia::Environment::Property->new({}); my $parent = Bugzilla::Testopia::Environment::Element->new($id); return unless $parent->canedit; $property->{'element_id'} = $id; $property->{'name'} = "New property " . $property->new_property_count; $property->{'validexp'} = ""; my $new_pid = $property->store(); my $json = '{title:"' . $property->{'name'} . '",'; $json .= 'objectId:"' . $new_pid . '",'; $json .= 'widgetId:"property' . $new_pid . '",'; $json .= 'childIconSrc:"testopia/img/triangle.gif",'; $json .= 'actionsDisabled:["addCategory","addElement","addProperty"]}'; print $json; } sub add_validexp{ my ($id) = (@_); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->canedit; my $exp = $property->validexp; $exp ? $property->update_property_validexp($exp . "|New value") : $property->update_property_validexp("New value"); my $json .= '{title: "New value",'; $json .= 'objectId:"' . $id . '~New Value",'; $json .= 'widgetId:"validexp' . $id . '~New Value",'; $json .= 'actionsDisabled:["addCategory","addElement","addProperty","addValue","remove"]}'; print $json; } ############################# ### Delete Helper Methods ### ############################# sub delete_category{ my ($id) = (@_); my $category = Bugzilla::Testopia::Environment::Category->new($id); return unless $category->candelete; my $success = $category->obliterate; print $success == 1 ? "true" : "false"; } sub delete_element{ my ($id) = (@_); my $element = Bugzilla::Testopia::Environment::Element->new($id); return unless $element->candelete; my $success = $element->obliterate; print $success == 1 ? "true" : "false"; } sub delete_property{ my ($id) = (@_); my $property = Bugzilla::Testopia::Environment::Property->new($id); return unless $property->candelete; my $success = $property->obliterate; print $success == 1 ? "true" : "false"; } sub delete_validexp{ # $id, $type my ($id) = (@_); $id =~ /^(\d+)~/; my $property = Bugzilla::Testopia::Environment::Property->new($1); return unless $property->candelete; my %values; foreach my $v (split /\|/, $property->validexp){ $values{$v} = 1; } $id =~ /~(.+)$/; my $value = $1; my $deleted = delete $values{$value}; my $exp = join("|", keys %values); $property->update_property_validexp($exp); print $deleted ? "true" : "false"; }