Advertisement
Guest User

TestRail Redmine custom plugin

a guest
Jun 5th, 2015
122
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 20.32 KB | None | 0 0
  1. <?php if (!defined('ROOTPATH')) exit('No direct script access allowed'); ?>
  2. <?php
  3.  
  4.  
  5.  
  6. #Include the FirePHP class
  7. require_once('FirePHPCore/FirePHP.class.php');
  8. #Start buffering the output. Not required if output_buffering is set on in php.ini file
  9. ob_start();
  10.  
  11. /**
  12.  * Redmine Defect Plugin for TestRail
  13.  *
  14.  * Copyright Gurock Software GmbH. All rights reserved.
  15.  *
  16.  * This is the TestRail defect plugin for Redmine. Please see
  17.  * http://docs.gurock.com/testrail-integration/defects-plugins for
  18.  * more information about TestRail's defect plugins.
  19.  *
  20.  * http://www.gurock.com/testrail/
  21.  */
  22.  
  23. class Redmine_custom_defect_plugin extends Defect_plugin
  24. {
  25.     private $_api;
  26.    
  27.     private $_address;
  28.     private $_user;
  29.     private $_password;
  30.    
  31.     private $_apikey;
  32.    
  33.     private $_is_legacy = false;
  34.     private $_trackers;
  35.     private $_categories;
  36.  
  37.    
  38.     private static $_meta = array(
  39.         'author' => 'Gurock Software',
  40.         'version' => '1.0',
  41.         'description' => 'Redmine defect plugin for TestRail',
  42.         'can_push' => true,
  43.         'can_lookup' => true,
  44.         'default_config' =>
  45.             '; Please configure your Redmine connection below
  46. ; For Redmine versions older than 1.3, you need to
  47. ; activate the legacy mode of this plugin. Please
  48. ; contact the Gurock Software support in case you
  49. ; have any questions or refer to the documentation:
  50. ; http://on.gurock.com/redmine35
  51. [connection]
  52. address=http://10.3.1.152:81/redmine/
  53. apikey=%redmine_apikey%'
  54.  
  55.     );
  56.    
  57.     public function get_meta()
  58.     {
  59.         return self::$_meta;
  60.     }
  61.    
  62.     // *********************************************************
  63.     // CONFIGURATION
  64.     // *********************************************************
  65.    
  66.     public function validate_config($config)
  67.     {
  68.         $ini = ini::parse($config);
  69.        
  70.         if (!isset($ini['connection']))
  71.         {
  72.             throw new ValidationException('Missing [connection] group');
  73.         }
  74.        
  75.         //$keys = array('address', 'user', 'password');
  76.         $keys = array('address','apikey');
  77.         // Check required values for existance
  78.         foreach ($keys as $key)
  79.         {
  80.             if (!isset($ini['connection'][$key]) ||
  81.                 !$ini['connection'][$key])
  82.             {
  83.                 throw new ValidationException(
  84.                     "Missing configuration for key '$key'"
  85.                 );
  86.             }
  87.         }
  88.        
  89.         $address = $ini['connection']['address'];
  90.        
  91.         // Check whether the address is a valid url (syntax only)
  92.         if (!check::url($address))
  93.         {
  94.             throw new ValidationException('Address is not a valid url');
  95.         }
  96.  
  97.         if (isset($ini['connection']['mode']))
  98.         {
  99.             // Mode must be set to 'legacy' when available.
  100.             if ($ini['connection']['mode'] != 'legacy')
  101.             {
  102.                 throw new ValidationException(
  103.                     'Mode given but not set to "legacy"'
  104.                 );
  105.             }
  106.  
  107.             if (!isset($ini['trackers']))
  108.             {
  109.                 throw new ValidationException(
  110.                     'Using legacy mode but [trackers] is missing'
  111.                 );
  112.             }
  113.  
  114.             if (!isset($ini['categories']))
  115.             {
  116.                 throw new ValidationException(
  117.                     'Using legacy mode but [categories] is missing'
  118.                 );
  119.             }
  120.         }
  121.     }
  122.    
  123.     public function configure($config)
  124.     {
  125.         $ini = ini::parse($config);
  126.         $this->_address = str::slash($ini['connection']['address']);
  127.         //$this->_user = $ini['connection']['user'];
  128.         //$this->_password = $ini['connection']['password'];
  129.         $this->_apikey = $ini['connection']['apikey'];
  130.  
  131.         if (isset($ini['connection']['mode']))
  132.         {
  133.             $this->_is_legacy = true;
  134.             $this->_trackers = $ini['trackers'];
  135.             $this->_categories = $this->_parse_categories(
  136.                 $ini['categories']);
  137.         }
  138.     }
  139.    
  140.     private function _parse_categories($ini)
  141.     {
  142.         $categories = array();
  143.  
  144.         // Uses the given ini section with keys 'project_id.item_id'
  145.         // to create a category key => value mapping for the given
  146.         // projects.
  147.         foreach ($ini as $key => $value)
  148.         {
  149.             if (preg_match('/^([^\.]+)\.([^\.]+)$/', $key, $matches))
  150.             {
  151.                 $project_id = (int) $matches[1];
  152.                 $item_id = (int) $matches[2];
  153.                 $categories[$project_id][$item_id] = $value;
  154.             }
  155.         }
  156.  
  157.         return $categories;
  158.     }
  159.  
  160.     // *********************************************************
  161.     // API / CONNECTION
  162.     // *********************************************************
  163.    
  164.     private function _get_api()
  165.     {
  166.         if ($this->_api)
  167.         {
  168.             return $this->_api;
  169.         }
  170.        
  171.         $this->_api = new Redmine_api(
  172.             $this->_address, $this->_apikey);
  173.             //$this->_user,
  174.             //$this->_password);
  175.        
  176.         return $this->_api;
  177.     }
  178.    
  179.     // *********************************************************
  180.     // PUSH
  181.     // *********************************************************
  182.  
  183.     public function prepare_push($context)
  184.     {
  185.         // Return a form with the following fields/properties
  186.         return array(
  187.             'fields' => array(
  188.                 'subject' => array(
  189.                     'type' => 'string',
  190.                     'label' => 'Subject',
  191.                     'required' => true,
  192.                     'size' => 'full'
  193.                 ),
  194.                 'tracker' => array(
  195.                     'type' => 'dropdown',
  196.                     'label' => 'Tracker',
  197.                     'required' => true,
  198.                     'remember' => true,
  199.                     'size' => 'compact'
  200.                 ),
  201.                 'project' => array(
  202.                     'type' => 'dropdown',
  203.                     'label' => 'Project',
  204.                     'required' => true,
  205.                     'remember' => true,
  206.                     'cascading' => true,
  207.                     'size' => 'compact'
  208.                 ),
  209.                 'category' => array(
  210.                     'type' => 'dropdown',
  211.                     'label' => 'Category',
  212.                     'remember' => true,
  213.                     'depends_on' => 'project',
  214.                     'size' => 'compact'
  215.                 ),
  216.                 'os' => array(
  217.                     'type' => 'dropdown',
  218.                     'label' => 'OS',
  219.                     'remember' => true,
  220.                     'size' => 'compact'
  221.                 ),
  222.                 'ostype' => array(
  223.                     'type' => 'dropdown',
  224.                     'label' => 'OS típusa',
  225.                     'remember' => true,
  226.                     'size' => 'compact'
  227.                 ),
  228.                 'reproducibility' => array(
  229.                     'type' => 'dropdown',
  230.                     'label' => 'Reprodukálhatóság',
  231.                     'remember' => true,
  232.                     'size' => 'compact'
  233.                 ),
  234.                 'severity' => array(
  235.                     'type' => 'dropdown',
  236.                     'label' => 'Súlyosság',
  237.                     'remember' => true,
  238.                     'size' => 'compact'
  239.                 ),             
  240.                 'stepstoreproduce' => array(
  241.                     'type' => 'text',
  242.                     'label' => 'Hibához szükséges lépések',
  243.                     'rows' => 10
  244.                 ),
  245.                 'description' => array(
  246.                     'type' => 'text',
  247.                     'label' => 'Description',
  248.                     'rows' => 10
  249.                 )
  250.             )
  251.         );
  252.     }
  253.    
  254.     private function _get_subject_default($context)
  255.     {      
  256.         $test = current($context['tests']);
  257.         $subject = 'Failed test: ' . $test->case->title;
  258.        
  259.         if ($context['test_count'] > 1)
  260.         {
  261.             $subject .= ' (+others)';
  262.         }
  263.        
  264.         return $subject;
  265.     }
  266.    
  267.     private function _get_description_default($context)
  268.     {
  269.         return $context['test_change']->description;
  270.     }
  271.    
  272.     private function _to_id_name_lookup($items)
  273.     {
  274.         $result = array();
  275.         foreach ($items as $item)
  276.         {
  277.             $result[$item->id] = $item->name;
  278.         }
  279.         return $result;
  280.     }
  281.    
  282.     //Get all possible value for the custom field
  283.     private function _get_possible_values($items, $name)
  284.     {
  285.         #get a firePHP variable reference
  286.         $firephp = FirePHP::getInstance(true);
  287.         $firephp->log($items, '_get_possible_values for '.$name.': items');
  288.    
  289.         $result = array();
  290.         foreach ($items as $item)
  291.         {      
  292.             if($item->name == $name)
  293.             {
  294.                 //$i = 0;          
  295.                 foreach($item->possible_values as $value)
  296.                 {              
  297.                     $result[$value->value] = $value->value;
  298.                     //$i++;
  299.                 }
  300.             }
  301.         }  
  302.         $firephp->log($result, '_get_possible_values for '.$name.': result');      
  303.         return $result;    
  304.     }
  305.  
  306.     private function _get_trackers($api)
  307.     {
  308.         // In legacy mode for Redmine versions older than 1.3, we use
  309.         // the user-configured values for the trackers. Otherwise,
  310.         // we can just use the API.
  311.         if ($this->_is_legacy)
  312.         {
  313.             if (is_array($this->_trackers))
  314.             {                      
  315.                 return $this->_trackers;
  316.             }
  317.             else
  318.             {
  319.                 return null;
  320.             }
  321.         }
  322.         else
  323.         {
  324.             return $this->_to_id_name_lookup(
  325.                 $api->get_trackers()
  326.             );
  327.         }
  328.     }
  329.  
  330.     private function _get_categories($api, $project_id)
  331.     {
  332.         // In legacy mode for Redmine versions older than 1.3, we use
  333.         // the user-configured values for the categories. Otherwise,
  334.         // we can just use the API.
  335.         if ($this->_is_legacy)
  336.         {
  337.             $categories = arr::get($this->_categories, $project_id);
  338.  
  339.             if (!is_array($categories))
  340.             {
  341.                 return null;
  342.             }
  343.  
  344.             return $categories;
  345.         }
  346.         else
  347.         {
  348.             return $this->_to_id_name_lookup(
  349.                 $api->get_categories($project_id)
  350.             );
  351.         }
  352.     }
  353.    
  354.     #GET custom field possible values from redmine
  355.    
  356.     private function _get_operating_systems($api)
  357.     {
  358.         return $this->_get_possible_values(
  359.                 $api->get_custom_fields(), 'OS'
  360.             );
  361.     }
  362.    
  363.     private function _get_operating_systems_types($api)
  364.     {
  365.         return $this->_get_possible_values(
  366.                 $api->get_custom_fields(), 'OS típusa'
  367.             );
  368.     }
  369.    
  370.     private function _get_reproducibility($api)
  371.     {
  372.         return $this->_get_possible_values(
  373.                 $api->get_custom_fields(), 'Reprodukálhatóság'
  374.             );
  375.     }
  376.    
  377.     private function _get_severity($api)
  378.     {
  379.         return $this->_get_possible_values(
  380.                 $api->get_custom_fields(), 'Súlyosság'
  381.             );
  382.     }
  383.  
  384.     private function _get_stepstoreproduce($api)
  385.     {
  386.         return $this->_get_possible_values(
  387.                 $api->get_custom_fields(), 'Hibához szükséges lépések'
  388.             );
  389.     }  
  390.        
  391.     public function prepare_field($context, $input, $field)
  392.     {
  393.         $data = array();
  394.        
  395.         // Take into account the preferences of the user, but only
  396.         // for the initial form rendering (not for dynamic loads).
  397.         if ($context['event'] == 'prepare')
  398.         {
  399.             $prefs = arr::get($context, 'preferences');
  400.         }
  401.         else
  402.         {
  403.             $prefs = null;
  404.         }
  405.        
  406.         // Process those fields that do not need a connection to the
  407.         // Redmine installation.       
  408.         if ($field == 'subject' || $field == 'description')
  409.         {
  410.             switch ($field)
  411.             {
  412.                 case 'subject':
  413.                     $data['default'] = $this->_get_subject_default(
  414.                         $context);
  415.                     break;
  416.                    
  417.                 case 'description':
  418.                     $data['default'] = $this->_get_description_default(
  419.                         $context);
  420.                     break;             
  421.             }
  422.        
  423.             return $data;
  424.         }
  425.        
  426.         // And then try to connect/login (in case we haven't set up a
  427.         // working connection previously in this request) and process
  428.         // the remaining fields.
  429.         $api = $this->_get_api();
  430.        
  431.         switch ($field)
  432.         {
  433.             case 'tracker':
  434.                 $data['default'] = arr::get($prefs, 'tracker');
  435.                 $data['options'] = $this->_get_trackers($api);
  436.                 break;
  437.  
  438.             case 'project':
  439.                 $data['default'] = arr::get($prefs, 'project');
  440.                 $data['options'] = $this->_to_id_name_lookup(
  441.                     $api->get_projects()
  442.                 );
  443.                 break;
  444.  
  445.             case 'category':
  446.                 if (isset($input['project']))
  447.                 {
  448.                     $data['default'] = arr::get($prefs, 'category');
  449.                     $data['options'] = $this->_get_categories($api,
  450.                         $input['project']);
  451.                 }
  452.                 break;
  453.                
  454.             case 'os':
  455.                 $data['default'] = arr::get($prefs, 'os');
  456.                 $data['options'] = $this->_get_operating_systems($api);
  457.                 break;
  458.                
  459.             case 'ostype':
  460.                 $data['default'] = arr::get($prefs, 'ostype');
  461.                 $data['options'] = $this->_get_operating_systems_types($api);
  462.                 break;
  463.                
  464.             case 'reproducibility':
  465.                 $data['default'] = arr::get($prefs, 'reproducibility');
  466.                 $data['options'] = $this->_get_reproducibility($api);
  467.                 break;
  468.                
  469.             case 'severity':
  470.                 $data['default'] = arr::get($prefs, 'severity');
  471.                 $data['options'] = $this->_get_severity($api);
  472.                 break;
  473.                            
  474.             case 'stepstoreproduce':
  475.                 $data['default'] = arr::get($prefs, 'stepstoreproduce');
  476.                 //$data['options'] = $this->_get_stepstoreproduce($api);
  477.                 break;
  478.         }
  479.        
  480.         return $data;
  481.     }
  482.    
  483.     public function validate_push($context, $input)
  484.     {
  485.     }
  486.  
  487.     public function push($context, $input)
  488.     {
  489.         $api = $this->_get_api();
  490.        
  491.         #get a firePHP variable reference
  492.         $firephp = FirePHP::getInstance(true);
  493.         $firephp->log($context, 'push: context');
  494.         $firephp->log($input, 'push: input');
  495.        
  496.         return $api->add_issue($input);
  497.     }
  498.    
  499.     // *********************************************************
  500.     // LOOKUP
  501.     // *********************************************************
  502.    
  503.     public function lookup($defect_id)
  504.     {
  505.         $api = $this->_get_api();
  506.         $issue = $api->get_issue($defect_id);
  507.  
  508.         $status_id = GI_DEFECTS_STATUS_OPEN;
  509.        
  510.         if (isset($issue->status))
  511.         {
  512.             $status = $issue->status->name;
  513.            
  514.             // Redmine's status API is only available in Redmine 1.3
  515.             // or later, unfortunately, so we can only try to guess
  516.             // by its name.
  517.             switch (str::to_lower($status))
  518.             {
  519.                 case 'resolved':
  520.                     $status_id = GI_DEFECTS_STATUS_RESOLVED;
  521.                     break;
  522.  
  523.                 case 'closed':
  524.                     $status_id = GI_DEFECTS_STATUS_CLOSED;
  525.                     break;
  526.             }
  527.         }
  528.         else
  529.         {
  530.             $status = null;
  531.         }
  532.        
  533.         if (isset($issue->description) && $issue->description)
  534.         {
  535.             $description = str::format(
  536.                 '<div class="monospace">{0}</div>',
  537.                 nl2br(
  538.                     html::link_urls(
  539.                         h($issue->description)
  540.                     )
  541.                 )
  542.             );
  543.         }
  544.         else
  545.         {
  546.             $description = null;
  547.         }
  548.        
  549.         // Add some important attributes for the issue such as the
  550.         // current status and project.
  551.        
  552.         $attributes = array();
  553.        
  554.         if (isset($issue->tracker))
  555.         {
  556.             $attributes['Tracker'] = h($issue->tracker->name);
  557.         }
  558.  
  559.         if ($status)
  560.         {
  561.             $attributes['Status'] = h($status);
  562.         }
  563.  
  564.         if (isset($issue->project))
  565.         {
  566.             // Add a link back to the project (issue list).
  567.             $attributes['Project'] = str::format(
  568.                 '<a target="_blank" href="{0}projects/{1}">{2}</a>',
  569.                 a($this->_address),
  570.                 a($issue->project->id),
  571.                 h($issue->project->name)
  572.             );
  573.         }
  574.  
  575.         if (isset($issue->category))
  576.         {
  577.             $attributes['Category'] = h($issue->category->name);
  578.         }
  579.        
  580.         #get a firePHP variable reference
  581.         $firephp = FirePHP::getInstance(true);
  582.         $firephp->log($issue, 'lookup: issue');
  583.        
  584.         //Gets all the custom fields and their content
  585.         if (isset($issue->custom_fields))
  586.         {
  587.             foreach($issue->custom_fields as $field){
  588.                 $attributes[$field->name] = h($field->value);  
  589.             }
  590.         }
  591.        
  592.         return array(
  593.             'id' => $defect_id,
  594.             'url' => str::format(
  595.                 '{0}issues/{1}',
  596.                 $this->_address,
  597.                 $defect_id
  598.             ),
  599.             'title' => $issue->subject,
  600.             'status_id' => $status_id,
  601.             'status' => $status,
  602.             'description' => $description,
  603.             'attributes' => $attributes
  604.         );
  605.     }
  606. }
  607.  
  608. /**
  609.  * Redmine API
  610.  *
  611.  * Wrapper class for the Redmine API with functions for retrieving
  612.  * projects, bugs etc. from a Redmine installation.
  613.  */
  614. class Redmine_api
  615. {
  616.     private $_address;
  617.     //private $_user;
  618.     //private $_password;
  619.     private $_apikey;
  620.     private $_version;
  621.     private $_curl;
  622.     private $_custom_fields;
  623.    
  624.     /**
  625.      * Construct
  626.      *
  627.      * Initializes a new Redmine API object. Expects the web address
  628.      * of the Redmine installation including http or https prefix.
  629.      */
  630.     //public function __construct($address, $user, $password)
  631.     public function __construct($address, $apikey)
  632.     {
  633.         $this->_address = str::slash($address);
  634.         //$this->_user = $user;
  635.         //$this->_password = $password;
  636.         $this->_apikey = $apikey;
  637.     }
  638.    
  639.     private function _throw_error($format, $params = null)
  640.     {
  641.         $args = func_get_args();
  642.         $format = array_shift($args);
  643.        
  644.         if (count($args) > 0)
  645.         {
  646.             $message = str::formatv($format, $args);
  647.         }
  648.         else
  649.         {
  650.             $message = $format;
  651.         }
  652.        
  653.         throw new RedmineException($message);
  654.     }
  655.    
  656.     private function _send_command($method, $command, $data = array())
  657.     {
  658.         $url = $this->_address . $command . '.json';
  659.  
  660.         if ($method == 'GET')
  661.         {
  662.             $url .= '?limit=100';
  663.         }
  664.  
  665.         return $this->_send_request($method, $url, $data);
  666.     }
  667.    
  668.     private function _send_request($method, $url, $data)
  669.     {
  670.         if (!$this->_curl)
  671.         {
  672.             // Initialize the cURL handle. We re-use this handle to
  673.             // make use of Keep-Alive, if possible.
  674.             $this->_curl = http::open();
  675.         }
  676.  
  677.         $response = http::request_ex(
  678.             $this->_curl,
  679.             $method,
  680.             $url,
  681.             array(
  682.                 'data' => $data,
  683.                 'headers' => array(
  684.                     'Content-Type' => 'application/json',
  685.                     'X-Redmine-API-Key' => $this->_apikey
  686.                 )
  687.                 // 'user' => $this->_user,
  688.                 // 'password' => $this->_password,
  689.                 // 'headers' => array(
  690.                     // 'Content-Type' => 'application/json'
  691.                 // ) 'X-Redmine-API-Key' => $this->_apikey
  692.             )
  693.         );
  694.             #get a firePHP variable reference
  695.             $firephp = FirePHP::getInstance(true);
  696.             $firephp->log($response, '_send_request: response');
  697.  
  698.         // In case debug logging is enabled, we append the data
  699.         // we've sent and the entire request/response to the log.
  700.         if (logger::is_on(GI_LOG_LEVEL_DEBUG))
  701.         {
  702.             logger::debugr('$data', $data);
  703.             logger::debugr('$response', $response);
  704.         }
  705.        
  706.         $obj = json::decode($response->content);
  707.        
  708.         if ($response->code != 200)
  709.         {
  710.             if ($response->code != 201) // Created
  711.             {
  712.                 $this->_throw_error(
  713.                     'Invalid HTTP code ({0}). Please check your user/' .
  714.                     'password and that the API is enabled in Redmine.',
  715.                     $response->code
  716.                 );
  717.             }
  718.         }      
  719.         return $obj;
  720.     }
  721.  
  722.     /**
  723.      * Get Issue
  724.      *
  725.      * Gets an existing issue from the Redmine installation and
  726.      * returns it. The resulting issue object has various properties
  727.      * such as the subject, description, project etc.
  728.      */  
  729.     public function get_issue($issue_id)
  730.     {
  731.         $response = $this->_send_command(
  732.             'GET', 'issues/' . urlencode($issue_id)
  733.         );
  734.        
  735.         return $response->issue;
  736.     }
  737.    
  738.     /**
  739.      * Get Trackers
  740.      *
  741.      * Gets the available trackers for the Redmine installation.
  742.      * Trackers are returned as array of objects, each with its ID
  743.      * and name. Requires Redmine >= 1.3.
  744.      */
  745.     public function get_trackers()
  746.     {
  747.         $response = $this->_send_command('GET', 'trackers');
  748.         return $response->trackers;
  749.     }
  750.  
  751.     /**
  752.      * Get Projects
  753.      *
  754.      * Gets the available projects for the Redmine installation.
  755.      * Projects are returned as array of objects, each with its ID
  756.      * and name.     
  757.      */
  758.     public function get_projects()
  759.     {
  760.         $response = $this->_send_command('GET', 'projects');
  761.         return $response->projects;
  762.     }
  763.    
  764.    
  765.     /**
  766.      * Get Custom fields
  767.      */
  768.     public function get_custom_fields()
  769.     {
  770.         $firephp = FirePHP::getInstance(true);
  771.         $firephp->log($_custom_fields, 'get_custom_fields: _custom_fields');
  772.         if(!isset($_custom_fields)){
  773.             $response = $this->_send_command('GET', 'custom_fields');
  774.            
  775.             $firephp->log($response, 'get_custom_fields: response');   
  776.             $_custom_fields = $response->custom_fields;        
  777.         }      
  778.         return $_custom_fields;
  779.     }
  780.    
  781.    
  782.     /**
  783.      * Get Categories
  784.      *
  785.      * Gets the available categories for the given project ID for the
  786.      * Redmine installation. Categories are returned as array of
  787.      * objects, each with its ID and name. Requires Redmine >= 1.3.
  788.      */
  789.     public function get_categories($project_id)
  790.     {
  791.         $firephp = FirePHP::getInstance(true);
  792.         $firephp->log($project_id, 'get_categories: project_id');
  793.        
  794.         $response = $this->_send_command('GET',
  795.             "projects/$project_id/issue_categories");
  796.         $firephp->log($response, 'get_categories: response');
  797.         return $response->issue_categories;
  798.     }
  799.    
  800.     /**
  801.      * Add Issue
  802.      *
  803.      * Adds a new issue to the Redmine installation with the given
  804.      * parameters (subject, project etc.) and returns its ID.
  805.      *
  806.      * subject:     The title of the new issue
  807.      * tracker:     The ID of the tracker of the new issue (bug,
  808.      *              feature request etc.)
  809.      * project:     The ID of the project the issue should be added
  810.      *              to
  811.      * category:    The ID of the category the issue is added to
  812.      * description: The description of the new issue
  813.      */
  814.     public function add_issue($options)
  815.     {
  816.         $issue = obj::create();
  817.        
  818.         $issue->subject = $options['subject'];
  819.         $issue->description = $options['description'];
  820.         $issue->tracker_id = $options['tracker'];
  821.         $issue->project_id = $options['project'];
  822.        
  823.         $custom_fields_array = array();
  824.        
  825.         array_push($custom_fields_array, array('id' => 1 ,'value' => $options['os']));
  826.         array_push($custom_fields_array, array('id' => 5 ,'value' => $options['ostype']));
  827.         array_push($custom_fields_array, array('id' => 2 ,'value' => $options['reproducibility']));
  828.         array_push($custom_fields_array, array('id' => 3 ,'value' => $options['severity']));
  829.         array_push($custom_fields_array, array('id' => 4 ,'value' => $options['stepstoreproduce']));
  830.        
  831.         $issue->custom_fields = $custom_fields_array;
  832.        
  833.        
  834.         #get a firePHP variable reference
  835.         $firephp = FirePHP::getInstance(true);
  836.         $firephp->log($issue, 'add_issue: issue');
  837.        
  838.         if ($options['category'])
  839.         {
  840.             $issue->category_id = $options['category'];
  841.         }
  842.        
  843.         $data = json::encode(array('issue' => $issue));
  844.        
  845.         $firephp->log($data, 'add_issue: data');   
  846.        
  847.         $response = $this->_send_command('POST', 'issues', $data);
  848.         return $response->issue->id;
  849.        
  850.         $firephp->log($response, 'add_issue: response');   
  851.     }
  852. }
  853.  
  854. class RedmineException extends Exception
  855. {
  856. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement