/lib/functions/tlTestCaseFilterControl.class.php
PHP | 1511 lines | 831 code | 226 blank | 454 comment | 166 complexity | b99f357b3f875cfc13c477c6ea6be25a MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, GPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * TestLink Open Source Project - http://testlink.sourceforge.net/
- * This script is distributed under the GNU General Public License 2 or later.
- *
- * @package TestLink
- * @author Andreas Simon
- * @copyright 2006-2010, TestLink community
- * @version CVS: $Id: tlTestCaseFilterControl.class.php,v 1.18 2010/08/30 09:14:59 asimon83 Exp $
- * @link http://www.teamst.org/index.php
- * @filesource http://testlink.cvs.sourceforge.net/viewvc/testlink/testlink/lib/functions/tlTestCaseFilterControl.class.php?view=markup
- *
- * This class extends tlFilterPanel for the specific use with test case tree.
- * It holds the logic to be used at GUI level to manage a common set of settings and filters for test cases.
- *
- * This class is used from different navigator-frames (left frames with a test case tree in it)
- * with different modes for different features.
- * This is a little overview about its usage in TestLink:
- *
- * - planTCNavigator.php/tpl use it in "plan_mode" for these features:
- * --> assign test case execution
- * --> update linked test case versions
- * --> set urgent tests
- *
- * - execNavigator.php/tpl in "execution_mode"
- * --> test execution
- *
- * - planAddTCNavigator.php/tpl in "plan_add_mode"
- * --> add/remove test cases
- *
- * - listTestCases.php/tcTree.tpl in "edit_mode"
- * --> edit test specification
- * --> assign keywords
- * --> assign requirements
- *
- * @internal Revisions:
- *
- * 20100830 - asimon - BUGID 3726: store user's selection of build and platform
- * 20100811 - asimon - BUGID 3566: show/hide CF
- * 20100810 - asimon - added TC ID filter for Test Cases
- * 20100807 - franciscom - BUGID 3660
- * 20100727 - asimon - BUGID 3630 - syntax error in get_argument_string()
- * 20100716 - asimon - BUGID 3406 - changes on init_settings() and $mode_setting_mapping
- * 20100713 - asimon - fixed Drag&Drop error caused by init_filter_custom_fields()
- * 20100702 - asimon - fixed error in init_setting_testplan()
- * 20100701 - asimon - BUGID 3414 - additional work in init_filter_custom_fields()
- * 20100628 - asimon - removal of constants
- * 20100624 - asimon - CVS merge (experimental branch to HEAD)
- * 20100503 - asimon - start of implementation of filter panel class hierarchy
- * to simplify/generalize filter panel handling
- * for test cases and requirements
- */
-
- /*
- * --------------------------------------------------------
- * An important note on BUGID 3516 (request-URL too large):
- * --------------------------------------------------------
- *
- * That problem has been solved by attaching some data (the set of active filters, settings and
- * testcase IDs to show if filtering has been done) to session.
- *
- * Since a user can have the same feature open in multiple tabs, that alone is not enough to
- * solve this issue. When a user opens e.g. the test case execution page and sets filter options
- * there, then opens the same page in another tab, the data saved in session would also be
- * applied to this second tab although no filter options have been set there yet by the user.
- *
- * This has now been solved by a so called form token. This token is, on first opening of a
- * navigator frame, generated by the method generate_form_token() and then stored in a member
- * variable with the name $form_token. This token will be stored in an identically named hidden
- * input field within the HTML filter form, so it gets sent by POST to every called page.
- * It is also attached to the GET argument string returned by get_argument_string() that gets
- * passed to multiple JavaScript functions, which are used to open nodes from the tree in the
- * left frame in a new page in the right frame.
- *
- * So the token is used to identify (from pages within the right frame) the data that got stored
- * for them in session by the navigator page in the left frame.
- * If the navigator page calls itself (when the user presses one of the submit buttons in the form),
- * it sends the stored token via POST to itself. So the same token can be used again to store data
- * in session, instead of generating a new token blindly on every page call no matter where the
- * call comes from. But if the user opens a new tab, the new navigator page knows this because
- * no token has been sent to it - so it generates a new one.
- *
- * The data is saved in session in the form of an array like this example:
- *
- * [execution_mode] => Array // "mode" used by navigator
- * (
- * [1986901204] => Array // form token to identify the correct tab
- * (
- * [filter_keywords_filter_type] => Or // the active filters and settings,
- * [filter_result_result] => f // prefixed with "filter_" and "setting_"
- * [filter_result_method] => 3
- * [filter_result_build] => 71
- * [filter_assigned_user_include_unassigned] => 1
- * [filter_testcase_name] =>
- * [filter_toplevel_testsuite] => Array
- * (
- * )
- *
- * [filter_keywords] =>
- * [filter_priority] => 3
- * [filter_execution_type] => 2
- * [filter_assigned_user] => Array
- * (
- * [3] => 3
- * )
- *
- * [filter_custom_fields] =>
- * [setting_testplan] => 4990
- * [setting_build] => 71
- * [setting_platform] =>
- * [setting_refresh_tree_on_action] => 1
- * [testcases_to_show] => Array // The internal IDs of the test cases which
- * ( // where not filtered out by user's choices.
- * [0] => 1852 // This was the part which earlier caused
- * [1] => 60 // the error because of the too long URL.
- * [2] => 2039
- * [3] => 2033
- * [4] => 2065
- * [5] => 2159
- * [6] => 3733
- * )
- *
- * [timestamp] => 1277727920 // additional means to check age of session data
- * )
- * )
- *
- * The access to this data can be done in the following way from the right frame page:
- *
- * $form_token = isset($_REQUEST['form_token']) ? $_REQUEST['form_token'] : 0;
- * $mode = 'execution_mode';
- * $session_data = isset($_SESSION[$mode]) && isset($_SESSION[$mode][$form_token])
- * ? $_SESSION[$mode][$form_token] : null;
- *
- * The variable $session_data then holds the array with all the active filters,
- * settings and filtered test case IDs in it, or is null if nothing has been stored yet
- * in the session.
- *
- * But now we have another problem:
- * There can be one array for each mode in the session. In each of these arrays is a set of
- * further arrays with the form tokens as keys and the filter information in it.
- * If a user now opens the same page more than once in a row (by switching back and forth
- * between features or by using the same feature in multiple tabs) there can be more and more
- * arrays with filter information in this set of arrays.
- *
- * Because of this, an additional timestamp is written into each of these information arrays.
- * On each storage process that writes information into the session triggered by a call
- * to a navigator page, the timestamp gets refreshed if an old token has been reused or
- * it gets created with the creation of a new data array.
- *
- * This timestamp can be used to delete old arrays with information that is not needed anymore.
- * Since we have no means to otherwise detect the case that a user has closed the tab
- * and doesn't need this information in the session anymore, we have to determine the age of
- * those arrays with the timestamp and delete everything that is older than a certain given
- * threshold. This is done by the method delete_old_session_data() which is automatically called
- * from the contstructor of this class. It checks the age of all the saved
- * arrays inside the array for the active mode and then deletes everything that's older than
- * the given threshold. This threshold can be passed as a parameter to the method, otherwise a
- * default value of one hour is used.
- *
- * If a user logs out of TestLink, of course all this data in the session is deleted,
- * no matter if the one hour threshold has passed or not.
- * ------------------------------------------------------------------------------------------------
- */
-
- /**
- * This class extends tlFilterPanel for the specific use with the testcase tree.
- * It contains logic to be used at GUI level to manage
- * a common set of settings and filters for testcases.
- *
- * @author Andreas Simon
- * @package TestLink
- * @uses testplan
- * @uses exec_cf_mgr
- * @uses tlPlatform
- * @uses testcase
- */
- class tlTestCaseFilterControl extends tlFilterControl {
-
- /**
- * Testcase manager object.
- * Initialized not in constructor, only on first use to save resources.
- * @var testcase
- */
- private $tc_mgr = null;
-
- /**
- * Platform manager object.
- * Initialized not in constructor, only on first use to save resources.
- * @var tlPlatform
- */
- private $platform_mgr = null;
-
- /**
- * Custom field manager object.
- * Initialized not in constructor, only on first use to save resources.
- * @var exec_cf_mgr
- */
- private $exec_cf_mgr = null;
-
- /**
- * Testplan manager object.
- * Initialized not in constructor, only on first use to save resources.
- * @var testplan
- */
- private $testplan_mgr = null;
-
- /**
- * This array contains all possible filters.
- * It is used as a helper to iterate over all the filters in some loops.
- * It also sets options how and from where to load the parameters with
- * input fetching functions in init_args()-method.
- * Its keys are the names of the settings (class constants are used),
- * its values are the arrays for the input parser.
- * @var array
- */
- private $all_filters = array('filter_tc_id' => array("POST", tlInputParameter::STRING_N),
- 'filter_testcase_name' => array("POST", tlInputParameter::STRING_N),
- 'filter_toplevel_testsuite' => array("POST", tlInputParameter::STRING_N),
- 'filter_keywords' => array("POST", tlInputParameter::ARRAY_INT),
- 'filter_priority' => array("POST", tlInputParameter::INT_N),
- 'filter_execution_type' => array("POST", tlInputParameter::INT_N),
- 'filter_assigned_user' => array("POST", tlInputParameter::ARRAY_INT),
- 'filter_custom_fields' => array("POST", tlInputParameter::ARRAY_STRING_N),
- 'filter_result' => null); // result: no info here, divided into more parts
-
- /**
- * This array is used as an additional security measure. It maps all available
- * filters to the mode in which they can be used. If a user tries to
- * enable filters in config.inc.php which are not defined inside this array,
- * this will be simply ignored instead of trying to initialize the filter
- * no matter wether it has been implemented or not.
- * The keys inside this array are the modes defined above as class constants.
- * So it can be checked if a filter is available in a given mode without
- * relying only on the config parameter.
- * @var array
- */
- private $mode_filter_mapping = array('edit_mode' => array('filter_tc_id',
- 'filter_testcase_name',
- 'filter_toplevel_testsuite',
- 'filter_keywords',
- 'filter_execution_type',
- 'filter_custom_fields'),
- 'execution_mode' => array('filter_tc_id',
- 'filter_testcase_name',
- 'filter_toplevel_testsuite',
- 'filter_keywords',
- 'filter_priority',
- 'filter_execution_type',
- 'filter_assigned_user',
- 'filter_custom_fields',
- 'filter_result'),
- 'plan_mode' => array('filter_tc_id',
- 'filter_testcase_name',
- 'filter_toplevel_testsuite',
- 'filter_keywords',
- 'filter_priority',
- 'filter_execution_type',
- 'filter_custom_fields',
- 'filter_result'),
- 'plan_add_mode' => array('filter_tc_id',
- 'filter_testcase_name',
- 'filter_toplevel_testsuite',
- 'filter_keywords',
- 'filter_priority',
- 'filter_execution_type',
- 'filter_custom_fields'));
-
- /**
- * This array contains all possible settings. It is used as a helper
- * to later iterate over all possibilities in loops.
- * Its keys are the names of the settings, its values the arrays for the input parser.
- * @var array
- */
- private $all_settings = array('setting_testplan' => array("POST", tlInputParameter::INT_N),
- 'setting_build' => array("POST", tlInputParameter::INT_N),
- 'setting_platform' => array("POST", tlInputParameter::INT_N),
- 'setting_refresh_tree_on_action' => array("POST", tlInputParameter::CB_BOOL));
-
- /**
- * This array is used to map the modes to their available settings.
- * @var array
- */
- private $mode_setting_mapping = array('edit_mode' => array('setting_refresh_tree_on_action'),
- 'execution_mode' => array('setting_testplan',
- 'setting_build',
- 'setting_platform',
- 'setting_refresh_tree_on_action'),
- 'plan_mode' => array('setting_testplan',
- // BUGID 3406
- 'setting_build',
- 'setting_refresh_tree_on_action'),
- 'plan_add_mode' => array('setting_testplan',
- 'setting_refresh_tree_on_action'));
-
- /**
- * The mode used. Depending on the feature for which this class will be instantiated.
- * This mode defines which filter configuration will be loaded from config.inc.php
- * and therefore which filters will be loaded and used for the templates.
- * Value has to be one of the class constants for mode, default is edit mode.
- * @var string
- */
- private $mode = 'edit_mode';
-
- /**
- * The token that will be used to identify the relationship between left frame
- * (with navigator) and right frame (which displays execution of test case e.g.) in session.
- * @var string
- */
- public $form_token = null;
-
- /**
- *
- * @param database $dbHandler
- * @param string $mode can be edit_mode/execution_mode/plan_mode/plan_add_mode, depending on usage
- */
- public function __construct(&$dbHandler, $mode = 'edit_mode') {
-
- // set mode to define further actions before calling parent constructor
- $this->mode = array_key_exists($mode,$this->mode_filter_mapping) ? $mode : 'edit_mode';
-
- // Call to constructor of parent class tlFilterControl.
- // This already loads configuration and user input
- // and does all the remaining necessary method calls,
- // so no further method call is required here for initialization.
- parent::__construct($dbHandler);
-
- // delete any filter settings that may be left from previous calls in session
- $this->delete_own_session_data();
- $this->delete_old_session_data();
-
- $this->save_session_data();
- }
-
- public function __destruct() {
- parent::__destruct(); //destroys testproject manager
-
- // destroy member objects
- unset($this->tc_mgr);
- unset($this->testplan_mgr);
- unset($this->platform_mgr);
- unset($this->exec_cf_mgr);
- }
-
- /**
- * Reads the configuration from the configuration file specific for test cases,
- * additionally to those parts of the config which were already loaded by parent class.
- *
- */
- protected function read_config() {
-
- // some configuration reading already done in parent class
- parent::read_config();
-
- // load configuration for active mode only
- $this->configuration = config_get('tree_filter_cfg')->testcases->{$this->mode};
-
- // load also exec config - it is not only needed in exec mode
- $this->configuration->exec_cfg = config_get('exec_cfg');
-
- // some additional testcase configuration
- $this->configuration->tc_cfg = config_get('testcase_cfg');
-
- // is choice of advanced filter mode enabled?
- if (isset($this->configuration->advanced_filter_mode_choice)
- && $this->configuration->advanced_filter_mode_choice == ENABLED) {
- $this->filter_mode_choice_enabled = true;
- } else {
- $this->filter_mode_choice_enabled = false;
- }
-
- return tl::OK;
- } // end of method
-
- /**
- * Does what init_args() usually does in all scripts: Reads the user input
- * from request ($_GET and $_POST). Later configuration,
- * settings and filters get modified according to that user input.
- */
- protected function init_args() {
-
- // some common user input is already read in parent class
- parent::init_args();
-
- // add settings and filters to parameter info array for request parsers
- $params = array();
- foreach ($this->all_settings as $name => $info) {
- if (is_array($info)) {
- $params[$name] = $info;
- }
- }
- foreach ($this->all_filters as $name => $info) {
- if (is_array($info)) {
- $params[$name] = $info;
- }
- }
- I_PARAMS($params, $this->args);
-
- $type = 'filter_keywords_filter_type';
- $this->args->{$type} = (isset($_REQUEST[$type])) ? trim($_REQUEST[$type]) : 'Or';
-
- $extra_keys = array('filter_result_result',
- 'filter_result_method',
- 'filter_result_build');
-
- foreach ($extra_keys as $ek) {
- $this->args->{$ek} = (isset($_REQUEST[$ek])) ? $_REQUEST[$ek] : null;
- }
-
- $this->args->{'filter_assigned_user_include_unassigned'} =
- isset($_REQUEST['filter_assigned_user_include_unassigned']) ? 1 : 0;
-
- // got session token sent by form or do we have to generate a new one?
- $sent_token = null;
- $this->args->form_token = null;
- if (isset($_REQUEST['form_token'])) {
- // token got sent
- $sent_token = $_REQUEST['form_token'];
- }
- if (!is_null($sent_token) && isset($_SESSION[$this->mode][$sent_token])) {
- // sent token is valid
- $this->form_token = $sent_token;
- $this->args->form_token = $sent_token;
- } else {
- $this->generate_form_token();
- }
-
- // "feature" is needed for plan and edit modes
- $this->args->feature = isset($_REQUEST['feature']) ? trim($_REQUEST['feature']) : null;
-
- switch ($this->mode) {
-
- case 'plan_mode':
- switch($this->args->feature) {
- case 'planUpdateTC':
- case 'test_urgency':
- case 'tc_exec_assignment':
- // feature OK
- break;
-
- default:
- // feature not OK
- tLog("Wrong or missing GET argument 'feature'.", 'ERROR');
- exit();
- break;
- }
- break;
-
- case 'edit_mode':
- switch($this->args->feature) {
- case 'edit_tc':
- case 'keywordsAssign':
- case 'assignReqs':
- // feature OK
- break;
-
- default:
- // feature not OK
- tLog("Wrong or missing GET argument 'feature'.", 'ERROR');
- exit();
- break;
- }
- break;
- }
-
- } // end of method
-
- /**
- * Initializes all settings.
- * Iterates through all available settings and adds an array to $this->settings
- * for the active ones, sets the rest to false so this can be
- * checked from templates and elsewhere.
- * Then calls the initializing method for each still active setting.
- */
- protected function init_settings() {
- $at_least_one_active = false;
-
- foreach ($this->all_settings as $name => $info) {
- $init_method = "init_$name";
- if (in_array($name, $this->mode_setting_mapping[$this->mode])
- && method_exists($this, $init_method)) {
- // is valid, configured, exists and therefore can be used, so initialize this setting
- $this->$init_method();
- $at_least_one_active = true;
- } else {
- // is not needed, simply deactivate it by setting it to false in main array
- $this->settings[$name] = false;
- }
- }
-
- // special situation: the build setting is in plan mode only needed for one feature
- // BUGID 3406
- if ($this->mode == 'plan_mode' && $this->args->feature != 'tc_exec_assignment') {
- $this->settings['setting_build'] = false;
- }
-
- // if at least one active setting is left to display, switch settings panel on
- if ($at_least_one_active) {
- $this->display_settings = true;
- }
- }
-
- /**
- * Initialize all filters. (called by parent::__construct())
- * I'm double checking here with loaded configuration _and_ additional array
- * $mode_filter_mapping, set according to defined mode, because this can avoid errors in case
- * when users try to enable a filter in config that doesn't exist for a mode.
- * Effect: Only existing and implemented filters can be activated in config file.
- */
- protected function init_filters() {
- // In resulting data structure, all values have to be defined (at least initialized),
- // no matter wether they are wanted for filtering or not.
- $additional_filters_to_init = array('filter_keywords_filter_type',
- 'filter_result_result',
- 'filter_result_method',
- 'filter_result_build',
- 'filter_assigned_user_include_unassigned');
-
- // now nullify them
- foreach ($additional_filters_to_init as $filtername) {
- $this->active_filters[$filtername] = null;
- }
-
-
- // iterate through all filters and activate the needed ones
- $this->display_filters = false;
- foreach ($this->all_filters as $name => $info) {
- $init_method = "init_$name";
- if (in_array($name, $this->mode_filter_mapping[$this->mode]) &&
- method_exists($this, $init_method) && $this->configuration->{$name} == ENABLED) {
-
- $this->$init_method();
-
- // there is at least one filter item to display => switch panel on
- $this->display_filters = true;
-
- } else {
- // is not needed, deactivate filter by setting it to false in main array
- // and of course also in active filters array
- $this->filters[$name] = false;
- $this->active_filters[$name] = null;
- }
- }
-
- // add the important settings to active filter array
- foreach ($this->all_settings as $name => $info) {
- if ($this->settings[$name]) {
- $this->active_filters[$name] = $this->settings[$name]['selected'];
- } else {
- $this->active_filters[$name] = null;
- }
- }
- } // end of method
-
- /**
- * This method returns an object or array, containing all selections chosen
- * by the user for filtering.
- *
- * @return mixed $value Return value is either an array or stdClass object,
- * depending on active mode. It contains all filter values selected by the user.
- */
- protected function get_active_filters() {
- static $value = null; // serves as a kind of cache
- // if method is called more than once
-
- // convert array to stcClass if needed
- if (!$value) {
- switch ($this->mode) {
- case 'execution_mode':
- case 'plan_mode':
- // these features are generating an exec tree,
- // they need the filters as a stdClass object
- $value = (object) $this->active_filters;
- break;
-
- default:
- // otherwise simply return the array as-is
- $value = $this->active_filters;
- break;
- }
- }
-
- return $value;
- } // end of method
-
- public function set_testcases_to_show($testcases_to_show = null) {
- // update active_filters
- if (!is_null($testcases_to_show)) {
- $this->active_filters['testcases_to_show'] = $testcases_to_show;
- }
-
- // Since a new filter in active_filters has been set from outside class after
- // saving of session data has already happened in constructor,
- // we explicitly update data in session after this change here.
- $this->save_session_data();
- }
-
- /**
- * Active filters will be saved to $_SESSION.
- * If there already is data for the active mode and token, it will be overwritten.
- * This data will be read from pages in the right frame.
- * This solves the problems with too long URLs.
- * See issue 3516 in Mantis for a little bit more information/explanation.
- * The therefore caused new problem that would arise now if
- * a user uses the same feature simultaneously in multiple browser tabs
- * is solved be the additional measure of using a form token.
- *
- * @author Andreas Simon
- * @return $tl::OK
- */
- public function save_session_data() {
- if (!isset($_SESSION[$this->mode]) || is_null($_SESSION[$this->mode]) || !is_array($_SESSION[$this->mode])) {
- $_SESSION[$this->mode] = array();
- }
-
- $_SESSION[$this->mode][$this->form_token] = $this->active_filters;
- $_SESSION[$this->mode][$this->form_token]['timestamp'] = time();
-
- return tl::OK;
- }
-
- /**
- * Old filter data for active mode will be deleted from $_SESSION.
- * It happens automatically after a session has expired and a user therefore
- * has to log in again, but here we can configure an additional time limit
- * only for this special filter part in session data.
- *
- * @author Andreas Simon
- * @param int $token_validity_duration data older than given timespan will be deleted
- */
- public function delete_old_session_data($token_validity_duration = 0) {
- // TODO this duration could maybe also be configured in config/const.inc.php
-
- // how long shall the data remain in session before it will be deleted?
- if (!is_numeric($token_validity_duration) || $token_validity_duration <= 0) {
- $token_validity_duration = 60 * 60 * 1; // one hour as default
- }
-
- // delete all tokens from session that are older than given age
- if (isset($_SESSION[$this->mode]) && is_array($_SESSION[$this->mode])) {
- foreach ($_SESSION[$this->mode] as $token => $data) {
- if ($data['timestamp'] < (time() - $token_validity_duration)) {
- // too old, delete!
- unset($_SESSION[$this->mode][$token]);
- }
- }
- }
- }
-
- public function delete_own_session_data() {
- if (isset($_SESSION[$this->mode]) && isset($_SESSION[$this->mode][$this->form_token])) {
- unset($_SESSION[$this->mode][$this->form_token]);
- }
- }
-
- /**
- * Generates a form token, which will be used to identify the relationship
- * between left navigator-frame with its settings and right frame.
- */
- protected function generate_form_token() {
- // Notice: I am just generating an integer here for the token.
- // Since this is not any security relevant stuff like a password hash or similar,
- // but only a means to separate multiple tabs a single user opens, this should suffice.
- // If we should some day decide that an integer is not enough,
- // we just have to change this one method and everything will still work.
-
- $min = 1234567890; // not magic, just some large number so the tokens don't get too short
- $max = mt_getrandmax();
- $token = 0;
-
- // generate new tokens until we find one that doesn't exist yet
- do {
- $token = mt_rand($min, $max);
- } while (isset($_SESSION[$this->mode][$token]));
-
- $this->form_token = $token;
- }
-
- /**
- * Active filters will be formatted as a GET-argument string.
- *
- * @return string $string the formatted string with active filters
- */
- public function get_argument_string() {
- static $string = null; // cache for repeated calls of this method
-
- if (!$string) {
- $string = '';
-
- // important: the token with which the page in right frame can access data in session
- $string .= '&form_token=' . $this->form_token;
-
- if ($this->settings['setting_build']) {
- $string .= '&setting_build=' .
- $this->settings['setting_build']['selected'];
- }
-
- if ($this->settings['setting_platform']) {
- $string .= '&setting_platform=' .
- $this->settings['setting_platform']['selected'];
- }
-
- $keyword_list = null;
- if (is_array($this->active_filters['filter_keywords'])) {
- $keyword_list = implode(',', $this->active_filters['filter_keywords']);
- } else if ($this->active_filters['filter_keywords']) {
- $keyword_list = $this->active_filters['filter_keywords'];
- }
- if ($keyword_list) {
- $string .= '&filter_keywords=' . $keyword_list .
- '&filter_keywords_filter_type=' .
- $this->active_filters['filter_keywords_filter_type'];
- }
-
- if ($this->active_filters['filter_priority'] > 0) {
- $string .= '&filter_priority=' . $this->active_filters['filter_priority'];
- }
-
- if ($this->active_filters['filter_assigned_user']) {
- // 3630
- $unassigned = $this->active_filters['filter_assigned_user_include_unassigned'] ? '1' : '0';
- $string .= '&filter_assigned_user='.
- serialize($this->active_filters['filter_assigned_user']) .
- '&filter_assigned_user_include_unassigned=' . $unassigned;
- }
-
- if ($this->active_filters['filter_result_result']) {
- $string .= '&filter_result_result=' .
- serialize($this->active_filters['filter_result_result']) .
- '&filter_result_method=' .
- $this->active_filters['filter_result_method'] .
- '&filter_result_build=' .
- $this->active_filters['filter_result_build'];
- }
- }
-
- return $string;
- }
-
- /**
- * Build the tree menu for generation of JavaScript test case tree.
- * Depending on mode and user's selections in user interface,
- * either a completely filtered tree will be build and returned,
- * or only the minimal necessary data to "lazy load"
- * the objects in the tree by later Ajax calls.
- * No return value - all variables will be stored in gui object
- * which is passed by reference.
- *
- * @author Andreas Simon
- * @param object $gui Reference to GUI object (data will be written to it)
- */
- public function build_tree_menu(&$gui) {
-
- $tree_menu = null;
- $filters = $this->get_active_filters();
- $additional_info = null;
- $options = null;
- $loader = '';
- $children = "[]";
- $cookie_prefix = '';
-
- // by default, disable drag and drop, then later enable if needed
- $drag_and_drop = new stdClass();
- $drag_and_drop->enabled = false;
- $drag_and_drop->BackEndUrl = '';
- $drag_and_drop->useBeforeMoveNode = FALSE;
-
- if (!$this->testproject_mgr) {
- $this->testproject_mgr = new testproject($this->db);
- }
-
- $tc_prefix = $this->testproject_mgr->getTestCasePrefix($this->args->testproject_id);
-
- switch ($this->mode) {
-
- case 'plan_mode':
- // No lazy loading here.
-
- $additional_info = new stdClass();
- $additional_info->useCounters = CREATE_TC_STATUS_COUNTERS_OFF;
- $additional_info->useColours = COLOR_BY_TC_STATUS_OFF;
- $additional_info->testcases_colouring_by_selected_build = DISABLED;
-
- $filters->show_testsuite_contents = 1;
- $filters->hide_testcases = 0;
-
- if ($this->args->feature == 'test_urgency') {
- $filters->hide_testcases = 1;
- }
-
- list($tree_menu, $testcases_to_show) = generateExecTree($this->db,
- $gui->menuUrl,
- $this->args->testproject_id,
- $this->args->testproject_name,
- $this->args->testplan_id,
- $this->args->testplan_name,
- $filters,
- $additional_info);
-
- $this->set_testcases_to_show($testcases_to_show);
-
- $root_node = $tree_menu->rootnode;
- $children = $tree_menu->menustring ? $tree_menu->menustring : "[]";
- $cookie_prefix = $this->args->feature;
- break;
-
- case 'edit_mode':
-
- if ($gui->tree_drag_and_drop_enabled[$this->args->feature]) {
- $drag_and_drop->enabled = true;
- $drag_and_drop->BackEndUrl = $this->args->basehref .
- 'lib/ajax/dragdroptprojectnodes.php';
- $drag_and_drop->useBeforeMoveNode = false;
- }
-
- if ($this->do_filtering) {
- $options = array('forPrinting' => NOT_FOR_PRINTING,
- 'hideTestCases' => SHOW_TESTCASES,
- 'tc_action_enabled' => DO_ON_TESTCASE_CLICK,
- 'ignore_inactive_testcases' => DO_NOT_FILTER_INACTIVE_TESTCASES,
- 'exclude_branches' => null);
-
- $tree_menu = generateTestSpecTree($this->db, $this->args->testproject_id,
- $this->args->testproject_name,
- $gui->menuUrl, $filters, $options);
-
- $root_node = $tree_menu->rootnode;
- $children = $tree_menu->menustring ? $tree_menu->menustring : "[]";
- $cookie_prefix = $this->args->feature;
- } else {
- $loader = $this->args->basehref . 'lib/ajax/gettprojectnodes.php?' .
- "root_node={$this->args->testproject_id}&" .
- "tcprefix=" . urlencode($tc_prefix .
- $this->configuration->tc_cfg->glue_character);
-
- $tcase_qty = $this->testproject_mgr->count_testcases($this->args->testproject_id);
-
- $root_node = new stdClass();
- $root_node->href = "javascript:EP({$this->args->testproject_id})";
- $root_node->id = $this->args->testproject_id;
- $root_node->name = $this->args->testproject_name . " ($tcase_qty)";
- $root_node->testlink_node_type='testproject';
-
- $cookie_prefix = 'tproject_' . $root_node->id . "_";
- }
- break;
-
- case 'plan_add_mode':
-
- $cookie_prefix = "planaddtc_{$this->args->testproject_id}_{$this->args->user_id}_";
-
- if ($this->do_filtering) {
- $options = array('forPrinting' => NOT_FOR_PRINTING,
- 'hideTestCases' => HIDE_TESTCASES,
- 'tc_action_enabled' => ACTION_TESTCASE_DISABLE,
- 'ignore_inactive_testcases' => IGNORE_INACTIVE_TESTCASES,
- 'viewType' => 'testSpecTreeForTestPlan');
-
- $tree_menu = generateTestSpecTree($this->db,
- $this->args->testproject_id,
- $this->args->testproject_name,
- $gui->menuUrl,
- $filters,
- $options);
-
- $root_node = $tree_menu->rootnode;
- $children = $tree_menu->menustring ? $tree_menu->menustring : "[]";
- } else {
- $loader = $this->args->basehref . 'lib/ajax/gettprojectnodes.php?' .
- "root_node={$this->args->testproject_id}&show_tcases=0";
-
- $root_node = new stdClass();
- $root_node->href = "javascript:EP({$this->args->testproject_id})";
- $root_node->id = $this->args->testproject_id;
- $root_node->name = $this->args->testproject_name;
- $root_node->testlink_node_type = 'testproject';
- }
- break;
-
- case 'execution_mode':
- default:
- // No lazy loading here.
- // Filtering is always done in execution mode, no matter if user enters data or not,
- // since the user should usually never see the whole tree here.
- $additional_info = new stdClass();
- $filters->hide_testcases = false;
- $filters->show_testsuite_contents = $this->configuration->exec_cfg->show_testsuite_contents;
- $additional_info->useCounters = $this->configuration->exec_cfg->enable_tree_testcase_counters;
-
- $additional_info->useColours = new stdClass();
- $additional_info->useColours->testcases =
- $this->configuration->exec_cfg->enable_tree_testcases_colouring;
- $additional_info->useColours->counters =
- $this->configuration->exec_cfg->enable_tree_counters_colouring;
- $additional_info->testcases_colouring_by_selected_build =
- $this->configuration->exec_cfg->testcases_colouring_by_selected_build;
-
- list($tree_menu, $testcases_to_show) = generateExecTree($this->db,
- $gui->menuUrl,
- $this->args->testproject_id,
- $this->args->testproject_name,
- $this->args->testplan_id,
- $this->args->testplan_name,
- $filters,
- $additional_info);
-
- $this->set_testcases_to_show($testcases_to_show);
-
- $root_node = $tree_menu->rootnode;
- $children = $tree_menu->menustring ? $tree_menu->menustring : "[]";
- $cookie_prefix = 'exec_tplan_id_' . $this->args->testplan_id;
- break;
- }
-
- $gui->tree = $tree_menu;
-
- $gui->ajaxTree = new stdClass();
- $gui->ajaxTree->loader = $loader;
- $gui->ajaxTree->root_node = $root_node;
- $gui->ajaxTree->children = $children;
- $gui->ajaxTree->cookiePrefix = $cookie_prefix;
- $gui->ajaxTree->dragDrop = $drag_and_drop;
- } // end of method
-
- private function init_setting_refresh_tree_on_action() {
-
- $key = 'setting_refresh_tree_on_action';
- $hidden_key = 'hidden_setting_refresh_tree_on_action';
- $selection = 0;
-
- $this->settings[$key] = array();
- $this->settings[$key][$hidden_key] = false;
-
- // look where we can find the setting - POST, SESSION, config?
- if (isset($this->args->{$key})) {
- $selection = 1;
- } else if (isset($this->args->{$hidden_key})) {
- $selection = 0;
- } else if (isset($_SESSION[$key])) {
- $selection = $_SESSION[$key];
- } else {
- $spec_cfg = config_get('spec_cfg');
- $selection = ($spec_cfg->automatic_tree_refresh > 0) ? 1 : 0;
- }
-
- $this->settings[$key]['selected'] = $selection;
- $this->settings[$key][$hidden_key] = $selection;
- $_SESSION[$key] = $selection;
- } // end of method
-
- private function init_setting_build() {
-
- $key = 'setting_build';
- if (is_null($this->testplan_mgr)) {
- $this->testplan_mgr = new testplan($this->db);
- }
-
- $tplan_id = $this->settings['setting_testplan']['selected'];
-
- // when in plan mode (assigning execution), we want all builds,
- // otherwise only those which are active and open
- $active = ($this->mode == 'plan_mode') ? null : testplan::GET_ACTIVE_BUILD;
- $open = ($this->mode == 'plan_mode') ? null : testplan::GET_OPEN_BUILD;
-
- $this->settings[$key]['items'] = $this->testplan_mgr->get_builds_for_html_options($tplan_id, $active, $open);
- $tplan_builds = array_keys($this->settings[$key]['items']);
-
- // BUGID 3406 - depending on mode, we need different labels for this selector on GUI
- $label = ($this->mode == 'plan_mode') ? 'assign_build' : 'exec_build';
- $this->settings[$key]['label'] = lang_get($label);
-
- // if no build has been chosen by user, select newest build by default
- $newest_build_id = $this->testplan_mgr->get_max_build_id($tplan_id, $active, $open);
-
- // BUGID 3726
- $session_key = $tplan_id . '_stored_setting_build';
- $session_selection = isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : null;
-
- $this->args->{$key} = $this->args->{$key} > 0 ? $this->args->{$key} : $session_selection;
-
- if (!$this->args->{$key}) {
- $this->args->{$key} = $newest_build_id;
- }
-
- // only take build ID into account if it really is a build from this testplan
- $this->settings[$key]['selected'] = (in_array($this->args->{$key}, $tplan_builds)) ?
- $this->args->{$key} : $newest_build_id;
-
- // still no build selected? take first one from selection.
- if (!$this->settings[$key]['selected'] && sizeof($this->settings[$key]['items'])) {
- $this->settings[$key]['selected'] = end($tplan_builds);
- }
-
- $_SESSION[$session_key] = $this->settings[$key]['selected'];
- } // end of method
-
- private function init_setting_testplan() {
-
- if (is_null($this->testplan_mgr)) {
- $this->testplan_mgr = new testplan($this->db);
- }
-
- $key = 'setting_testplan';
- $testplans = $this->user->getAccessibleTestPlans($this->db, $this->args->testproject_id);
-
- if (isset($_SESSION['testplanID']) && $_SESSION['testplanID'] != $this->args->{$key}) {
- // testplan was changed, we need to reset all filters
- // --> they were chosen for another testplan, not this one!
- $this->args->reset_filters = true;
-
- // check if user is allowed to set chosen testplan before changing
- foreach ($testplans as $plan) {
- if ($plan['id'] == $this->args->{$key}) {
- setSessionTestPlan($plan);
- }
- }
- }
-
- // now load info from session
- $info = $this->testplan_mgr->get_by_id($_SESSION['testplanID']);
- $this->args->testplan_name = $info['name'];
- $this->args->testplan_id = $info['id'];
- $this->args->{$key} = $info['id'];
- $this->settings[$key]['selected'] = $info['id'];
-
- // Now get all selectable testplans for the user to display.
- // For execution, don't take testplans into selection which have no (active/open) builds!
- // For plan add mode, add every plan no matter if he has builds or not.
- foreach ($testplans as $plan) {
- $add_plan = $this->mode == 'plan_add_mode';
- if (!$add_plan) {
- $builds = $this->testplan_mgr->get_builds($plan['id'],
- testplan::GET_ACTIVE_BUILD,
- testplan::GET_OPEN_BUILD);
- $add_plan = (is_array($builds) && count($builds));
- }
-
- if ($add_plan) {
- $this->settings[$key]['items'][$plan['id']] = $plan['name'];
- }
- }
- } // end of method
-
- private function init_setting_platform() {
- if (!$this->platform_mgr) {
- $this->platform_mgr = new tlPlatform($this->db);
- }
- $key = 'setting_platform';
-
- $this->settings[$key] = array('items' => null, 'selected' => $this->args->{$key});
- $testplan_id = $this->settings['setting_testplan']['selected'];
-
- $this->settings[$key]['items'] = $this->platform_mgr->getLinkedToTestplanAsMap($testplan_id);
-
- // BUGID 3726
- $session_key = $testplan_id . '_stored_setting_platform';
- $session_selection = isset($_SESSION[$session_key]) ? $_SESSION[$session_key] : null;
-
- if (!$this->settings[$key]['selected']) {
- $this->settings[$key]['selected'] = $session_selection;
- }
-
- if (!isset($this->settings[$key]['items']) || !is_array($this->settings[$key]['items'])) {
- $this->settings[$key] = false;
- } else if (isset($this->settings[$key]['items']) && is_array($this->settings[$key]['items']) &&
- is_null($this->settings[$key]['selected'])) {
- // platforms exist, but none has been selected yet, so select first one
- $this->settings[$key]['selected'] = key($this->settings[$key]['items']);
- }
-
- $_SESSION[$session_key] = $this->settings[$key]['selected'];
- } // end of method
-
- private function init_filter_tc_id() {
- $key = 'filter_tc_id';
- $selection = $this->args->{$key};
- $internal_id = null;
-
- if (!$this->testproject_mgr) {
- $this->testproject_mgr = new testproject($this->db);
- }
- if (!$this->tc_mgr) {
- $this->tc_mgr = new testcase($this->db);
- }
-
- $tc_prefix = $this->testproject_mgr->getTestCasePrefix($this->args->testproject_id);
- $tc_prefix .= $this->configuration->tc_cfg->glue_character;
-
- if (!$selection || $selection == $tc_prefix || $this->args->reset_filters) {
- $selection = null;
- } else {
- $this->do_filtering = true;
- // we got the external ID here when filtering, but need the internal one
- $internal_id = $this->tc_mgr->getInternalID($selection);
- }
-
- $this->filters[$key] = array('selected' => $selection ? $selection : $tc_prefix);
- $this->active_filters[$key] = $internal_id;
- } // end of method
-
- private function init_filter_testcase_name() {
- $key = 'filter_testcase_name';
- $selection = $this->args->{$key};
-
- if (!$selection || $this->args->reset_filters) {
- $selection = null;
- } else {
- $this->do_filtering = true;
- }
-
- $this->filters[$key] = array('selected' => $selection);
- $this->active_filters[$key] = $selection;
- } // end of method
-
-
- private function init_filter_toplevel_testsuite() {
- if (!$this->testproject_mgr) {
- $this->testproject_mgr = new testproject($this->db);
- }
- $key = 'filter_toplevel_testsuite';
-
- $first_level_suites = $this->testproject_mgr->get_first_level_test_suites($this->args->testproject_id,
- 'smarty_html_options');
-
- $selection = $this->args->{$key};
- if (!$selection || $this->args->reset_filters) {
- $selection = null;
- } else {
- $this->do_filtering = true;
- }
-
- // this filter should only be visible if there are any top level testsuites
- $this->filters[$key] = null; // BUGID 3660
- if ($first_level_suites) {
- $this->filters[$key] = array('items' => array(0 => ''),
- 'selected' => $selection,
- 'exclude_branches' => array());
-
- foreach ($first_level_suites as $suite_id => $suite_name) {
- $this->filters[$key]['items'][$suite_id] = $suite_name;
-
- if ($selection && $suite_id != $selection) {
- $this->filters[$key]['exclude_branches'][$suite_id] = 'exclude_me';
- }
- }
-
- // Important: This is the only case in which active_filters contains the items
- // which have to be deleted from tree, instead of the other way around.
- $this->active_filters[$key] = $this->filters[$key]['exclude_branches'];
- } else {
- $this->active_filters[$key] = null;
- }
- } // end of method
-
- private function init_filter_keywords() {
- $key = 'filter_keywords';
- $type = 'filter_keywords_filter_type';
- $this->filters[$key] = false;
- $keywords = null;
-
- switch ($this->mode) {
- case 'edit_mode':
- // we need the keywords for the whole testproject
- if (!$this->testproject_mgr) {
- $this->testproject_mgr = new testproject($this->db);
- }
- $keywords = $this->testproject_mgr->get_keywords_map($this->args->testproject_id);
- break;
-
- default:
- // otherwise (not in edit mode), we want only keywords assigned to testplan
- if (!$this->testplan_mgr) {
- $this->testplan_mgr = new testplan($this->db);
- }
- $tplan_id = $this->settings['setting_testplan']['selected'];
- $keywords = $this->testplan_mgr->get_keywords_map($tplan_id, ' ORDER BY keyword ');
- break;
- }
-
- $selection = $this->args->{$key};
- $type_selection = $this->args->{$type};
-
- // are there any keywords?
- if (!is_null($keywords) && count($keywords)) {
- $this->filters[$key] = array();
-
- if (!$selection || !$type_selection || $this->args->reset_filters) {
- // default values for filter reset
- $selection = null;
- $type_selection = 'Or';
- } else {
- $this->do_filtering = true;
- }
-
- // data for the keywords themselves
- $this->filters[$key]['items'] = array($this->option_strings['any']) + $keywords;
- $this->filters[$key]['selected'] = $selection;
- $this->filters[$key]['size'] = min(count($this->filters[$key]['items']),
- self::ADVANCED_FILTER_ITEM_QUANTITY);
-
- // additional data for the filter type (logical and/or)
- $this->filters[$key][$type] = array();
- $this->filters[$key][$type]['items'] = array('Or' => lang_get('logical_or'),
- 'And…
Large files files are truncated, but you can click here to view the full file